roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4882 <p>
4883 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4884
4885 <p>
4886 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * RooJS Library 
6059  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6060  *
6061  * Licence LGPL 
6062  *
6063  */
6064  
6065 /**
6066  * @class Roo.Document
6067  * @extends Roo.util.Observable
6068  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6069  * 
6070  * @param {Object} config the methods and properties of the 'base' class for the application.
6071  * 
6072  *  Generic Page handler - implement this to start your app..
6073  * 
6074  * eg.
6075  *  MyProject = new Roo.Document({
6076         events : {
6077             'load' : true // your events..
6078         },
6079         listeners : {
6080             'ready' : function() {
6081                 // fired on Roo.onReady()
6082             }
6083         }
6084  * 
6085  */
6086 Roo.Document = function(cfg) {
6087      
6088     this.addEvents({ 
6089         'ready' : true
6090     });
6091     Roo.util.Observable.call(this,cfg);
6092     
6093     var _this = this;
6094     
6095     Roo.onReady(function() {
6096         _this.fireEvent('ready');
6097     },null,false);
6098     
6099     
6100 }
6101
6102 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.EventManager
6115  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6116  * several useful events directly.
6117  * See {@link Roo.EventObject} for more details on normalized event objects.
6118  * @singleton
6119  */
6120 Roo.EventManager = function(){
6121     var docReadyEvent, docReadyProcId, docReadyState = false;
6122     var resizeEvent, resizeTask, textEvent, textSize;
6123     var E = Roo.lib.Event;
6124     var D = Roo.lib.Dom;
6125
6126     
6127     
6128
6129     var fireDocReady = function(){
6130         if(!docReadyState){
6131             docReadyState = true;
6132             Roo.isReady = true;
6133             if(docReadyProcId){
6134                 clearInterval(docReadyProcId);
6135             }
6136             if(Roo.isGecko || Roo.isOpera) {
6137                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6138             }
6139             if(Roo.isIE){
6140                 var defer = document.getElementById("ie-deferred-loader");
6141                 if(defer){
6142                     defer.onreadystatechange = null;
6143                     defer.parentNode.removeChild(defer);
6144                 }
6145             }
6146             if(docReadyEvent){
6147                 docReadyEvent.fire();
6148                 docReadyEvent.clearListeners();
6149             }
6150         }
6151     };
6152     
6153     var initDocReady = function(){
6154         docReadyEvent = new Roo.util.Event();
6155         if(Roo.isGecko || Roo.isOpera) {
6156             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6157         }else if(Roo.isIE){
6158             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6159             var defer = document.getElementById("ie-deferred-loader");
6160             defer.onreadystatechange = function(){
6161                 if(this.readyState == "complete"){
6162                     fireDocReady();
6163                 }
6164             };
6165         }else if(Roo.isSafari){ 
6166             docReadyProcId = setInterval(function(){
6167                 var rs = document.readyState;
6168                 if(rs == "complete") {
6169                     fireDocReady();     
6170                  }
6171             }, 10);
6172         }
6173         // no matter what, make sure it fires on load
6174         E.on(window, "load", fireDocReady);
6175     };
6176
6177     var createBuffered = function(h, o){
6178         var task = new Roo.util.DelayedTask(h);
6179         return function(e){
6180             // create new event object impl so new events don't wipe out properties
6181             e = new Roo.EventObjectImpl(e);
6182             task.delay(o.buffer, h, null, [e]);
6183         };
6184     };
6185
6186     var createSingle = function(h, el, ename, fn){
6187         return function(e){
6188             Roo.EventManager.removeListener(el, ename, fn);
6189             h(e);
6190         };
6191     };
6192
6193     var createDelayed = function(h, o){
6194         return function(e){
6195             // create new event object impl so new events don't wipe out properties
6196             e = new Roo.EventObjectImpl(e);
6197             setTimeout(function(){
6198                 h(e);
6199             }, o.delay || 10);
6200         };
6201     };
6202     var transitionEndVal = false;
6203     
6204     var transitionEnd = function()
6205     {
6206         if (transitionEndVal) {
6207             return transitionEndVal;
6208         }
6209         var el = document.createElement('div');
6210
6211         var transEndEventNames = {
6212             WebkitTransition : 'webkitTransitionEnd',
6213             MozTransition    : 'transitionend',
6214             OTransition      : 'oTransitionEnd otransitionend',
6215             transition       : 'transitionend'
6216         };
6217     
6218         for (var name in transEndEventNames) {
6219             if (el.style[name] !== undefined) {
6220                 transitionEndVal = transEndEventNames[name];
6221                 return  transitionEndVal ;
6222             }
6223         }
6224     }
6225     
6226
6227     var listen = function(element, ename, opt, fn, scope){
6228         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6229         fn = fn || o.fn; scope = scope || o.scope;
6230         var el = Roo.getDom(element);
6231         
6232         
6233         if(!el){
6234             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6235         }
6236         
6237         if (ename == 'transitionend') {
6238             ename = transitionEnd();
6239         }
6240         var h = function(e){
6241             e = Roo.EventObject.setEvent(e);
6242             var t;
6243             if(o.delegate){
6244                 t = e.getTarget(o.delegate, el);
6245                 if(!t){
6246                     return;
6247                 }
6248             }else{
6249                 t = e.target;
6250             }
6251             if(o.stopEvent === true){
6252                 e.stopEvent();
6253             }
6254             if(o.preventDefault === true){
6255                e.preventDefault();
6256             }
6257             if(o.stopPropagation === true){
6258                 e.stopPropagation();
6259             }
6260
6261             if(o.normalized === false){
6262                 e = e.browserEvent;
6263             }
6264
6265             fn.call(scope || el, e, t, o);
6266         };
6267         if(o.delay){
6268             h = createDelayed(h, o);
6269         }
6270         if(o.single){
6271             h = createSingle(h, el, ename, fn);
6272         }
6273         if(o.buffer){
6274             h = createBuffered(h, o);
6275         }
6276         fn._handlers = fn._handlers || [];
6277         
6278         
6279         fn._handlers.push([Roo.id(el), ename, h]);
6280         
6281         
6282          
6283         E.on(el, ename, h);
6284         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6285             el.addEventListener("DOMMouseScroll", h, false);
6286             E.on(window, 'unload', function(){
6287                 el.removeEventListener("DOMMouseScroll", h, false);
6288             });
6289         }
6290         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6291             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6292         }
6293         return h;
6294     };
6295
6296     var stopListening = function(el, ename, fn){
6297         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6298         if(hds){
6299             for(var i = 0, len = hds.length; i < len; i++){
6300                 var h = hds[i];
6301                 if(h[0] == id && h[1] == ename){
6302                     hd = h[2];
6303                     hds.splice(i, 1);
6304                     break;
6305                 }
6306             }
6307         }
6308         E.un(el, ename, hd);
6309         el = Roo.getDom(el);
6310         if(ename == "mousewheel" && el.addEventListener){
6311             el.removeEventListener("DOMMouseScroll", hd, false);
6312         }
6313         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6314             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6315         }
6316     };
6317
6318     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6319     
6320     var pub = {
6321         
6322         
6323         /** 
6324          * Fix for doc tools
6325          * @scope Roo.EventManager
6326          */
6327         
6328         
6329         /** 
6330          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6331          * object with a Roo.EventObject
6332          * @param {Function} fn        The method the event invokes
6333          * @param {Object}   scope    An object that becomes the scope of the handler
6334          * @param {boolean}  override If true, the obj passed in becomes
6335          *                             the execution scope of the listener
6336          * @return {Function} The wrapped function
6337          * @deprecated
6338          */
6339         wrap : function(fn, scope, override){
6340             return function(e){
6341                 Roo.EventObject.setEvent(e);
6342                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6343             };
6344         },
6345         
6346         /**
6347      * Appends an event handler to an element (shorthand for addListener)
6348      * @param {String/HTMLElement}   element        The html element or id to assign the
6349      * @param {String}   eventName The type of event to listen for
6350      * @param {Function} handler The method the event invokes
6351      * @param {Object}   scope (optional) The scope in which to execute the handler
6352      * function. The handler function's "this" context.
6353      * @param {Object}   options (optional) An object containing handler configuration
6354      * properties. This may contain any of the following properties:<ul>
6355      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6356      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6357      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6358      * <li>preventDefault {Boolean} True to prevent the default action</li>
6359      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6360      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6361      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6362      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6363      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6364      * by the specified number of milliseconds. If the event fires again within that time, the original
6365      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6366      * </ul><br>
6367      * <p>
6368      * <b>Combining Options</b><br>
6369      * Using the options argument, it is possible to combine different types of listeners:<br>
6370      * <br>
6371      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6372      * Code:<pre><code>
6373 el.on('click', this.onClick, this, {
6374     single: true,
6375     delay: 100,
6376     stopEvent : true,
6377     forumId: 4
6378 });</code></pre>
6379      * <p>
6380      * <b>Attaching multiple handlers in 1 call</b><br>
6381       * The method also allows for a single argument to be passed which is a config object containing properties
6382      * which specify multiple handlers.
6383      * <p>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : {
6387         fn: this.onClick
6388         scope: this,
6389         delay: 100
6390     },
6391     'mouseover' : {
6392         fn: this.onMouseOver
6393         scope: this
6394     },
6395     'mouseout' : {
6396         fn: this.onMouseOut
6397         scope: this
6398     }
6399 });</code></pre>
6400      * <p>
6401      * Or a shorthand syntax:<br>
6402      * Code:<pre><code>
6403 el.on({
6404     'click' : this.onClick,
6405     'mouseover' : this.onMouseOver,
6406     'mouseout' : this.onMouseOut
6407     scope: this
6408 });</code></pre>
6409      */
6410         addListener : function(element, eventName, fn, scope, options){
6411             if(typeof eventName == "object"){
6412                 var o = eventName;
6413                 for(var e in o){
6414                     if(propRe.test(e)){
6415                         continue;
6416                     }
6417                     if(typeof o[e] == "function"){
6418                         // shared options
6419                         listen(element, e, o, o[e], o.scope);
6420                     }else{
6421                         // individual options
6422                         listen(element, e, o[e]);
6423                     }
6424                 }
6425                 return;
6426             }
6427             return listen(element, eventName, options, fn, scope);
6428         },
6429         
6430         /**
6431          * Removes an event handler
6432          *
6433          * @param {String/HTMLElement}   element        The id or html element to remove the 
6434          *                             event from
6435          * @param {String}   eventName     The type of event
6436          * @param {Function} fn
6437          * @return {Boolean} True if a listener was actually removed
6438          */
6439         removeListener : function(element, eventName, fn){
6440             return stopListening(element, eventName, fn);
6441         },
6442         
6443         /**
6444          * Fires when the document is ready (before onload and before images are loaded). Can be 
6445          * accessed shorthanded Roo.onReady().
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An  object that becomes the scope of the handler
6448          * @param {boolean}  options
6449          */
6450         onDocumentReady : function(fn, scope, options){
6451             if(docReadyState){ // if it already fired
6452                 docReadyEvent.addListener(fn, scope, options);
6453                 docReadyEvent.fire();
6454                 docReadyEvent.clearListeners();
6455                 return;
6456             }
6457             if(!docReadyEvent){
6458                 initDocReady();
6459             }
6460             docReadyEvent.addListener(fn, scope, options);
6461         },
6462         
6463         /**
6464          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6465          * @param {Function} fn        The method the event invokes
6466          * @param {Object}   scope    An object that becomes the scope of the handler
6467          * @param {boolean}  options
6468          */
6469         onWindowResize : function(fn, scope, options){
6470             if(!resizeEvent){
6471                 resizeEvent = new Roo.util.Event();
6472                 resizeTask = new Roo.util.DelayedTask(function(){
6473                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6474                 });
6475                 E.on(window, "resize", function(){
6476                     if(Roo.isIE){
6477                         resizeTask.delay(50);
6478                     }else{
6479                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                     }
6481                 });
6482             }
6483             resizeEvent.addListener(fn, scope, options);
6484         },
6485
6486         /**
6487          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6488          * @param {Function} fn        The method the event invokes
6489          * @param {Object}   scope    An object that becomes the scope of the handler
6490          * @param {boolean}  options
6491          */
6492         onTextResize : function(fn, scope, options){
6493             if(!textEvent){
6494                 textEvent = new Roo.util.Event();
6495                 var textEl = new Roo.Element(document.createElement('div'));
6496                 textEl.dom.className = 'x-text-resize';
6497                 textEl.dom.innerHTML = 'X';
6498                 textEl.appendTo(document.body);
6499                 textSize = textEl.dom.offsetHeight;
6500                 setInterval(function(){
6501                     if(textEl.dom.offsetHeight != textSize){
6502                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6503                     }
6504                 }, this.textResizeInterval);
6505             }
6506             textEvent.addListener(fn, scope, options);
6507         },
6508
6509         /**
6510          * Removes the passed window resize listener.
6511          * @param {Function} fn        The method the event invokes
6512          * @param {Object}   scope    The scope of handler
6513          */
6514         removeResizeListener : function(fn, scope){
6515             if(resizeEvent){
6516                 resizeEvent.removeListener(fn, scope);
6517             }
6518         },
6519
6520         // private
6521         fireResize : function(){
6522             if(resizeEvent){
6523                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6524             }   
6525         },
6526         /**
6527          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6528          */
6529         ieDeferSrc : false,
6530         /**
6531          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6532          */
6533         textResizeInterval : 50
6534     };
6535     
6536     /**
6537      * Fix for doc tools
6538      * @scopeAlias pub=Roo.EventManager
6539      */
6540     
6541      /**
6542      * Appends an event handler to an element (shorthand for addListener)
6543      * @param {String/HTMLElement}   element        The html element or id to assign the
6544      * @param {String}   eventName The type of event to listen for
6545      * @param {Function} handler The method the event invokes
6546      * @param {Object}   scope (optional) The scope in which to execute the handler
6547      * function. The handler function's "this" context.
6548      * @param {Object}   options (optional) An object containing handler configuration
6549      * properties. This may contain any of the following properties:<ul>
6550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6551      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6552      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6553      * <li>preventDefault {Boolean} True to prevent the default action</li>
6554      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6555      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6556      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6557      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6558      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6559      * by the specified number of milliseconds. If the event fires again within that time, the original
6560      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6561      * </ul><br>
6562      * <p>
6563      * <b>Combining Options</b><br>
6564      * Using the options argument, it is possible to combine different types of listeners:<br>
6565      * <br>
6566      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6567      * Code:<pre><code>
6568 el.on('click', this.onClick, this, {
6569     single: true,
6570     delay: 100,
6571     stopEvent : true,
6572     forumId: 4
6573 });</code></pre>
6574      * <p>
6575      * <b>Attaching multiple handlers in 1 call</b><br>
6576       * The method also allows for a single argument to be passed which is a config object containing properties
6577      * which specify multiple handlers.
6578      * <p>
6579      * Code:<pre><code>
6580 el.on({
6581     'click' : {
6582         fn: this.onClick
6583         scope: this,
6584         delay: 100
6585     },
6586     'mouseover' : {
6587         fn: this.onMouseOver
6588         scope: this
6589     },
6590     'mouseout' : {
6591         fn: this.onMouseOut
6592         scope: this
6593     }
6594 });</code></pre>
6595      * <p>
6596      * Or a shorthand syntax:<br>
6597      * Code:<pre><code>
6598 el.on({
6599     'click' : this.onClick,
6600     'mouseover' : this.onMouseOver,
6601     'mouseout' : this.onMouseOut
6602     scope: this
6603 });</code></pre>
6604      */
6605     pub.on = pub.addListener;
6606     pub.un = pub.removeListener;
6607
6608     pub.stoppedMouseDownEvent = new Roo.util.Event();
6609     return pub;
6610 }();
6611 /**
6612   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6613   * @param {Function} fn        The method the event invokes
6614   * @param {Object}   scope    An  object that becomes the scope of the handler
6615   * @param {boolean}  override If true, the obj passed in becomes
6616   *                             the execution scope of the listener
6617   * @member Roo
6618   * @method onReady
6619  */
6620 Roo.onReady = Roo.EventManager.onDocumentReady;
6621
6622 Roo.onReady(function(){
6623     var bd = Roo.get(document.body);
6624     if(!bd){ return; }
6625
6626     var cls = [
6627             Roo.isIE ? "roo-ie"
6628             : Roo.isGecko ? "roo-gecko"
6629             : Roo.isOpera ? "roo-opera"
6630             : Roo.isSafari ? "roo-safari" : ""];
6631
6632     if(Roo.isMac){
6633         cls.push("roo-mac");
6634     }
6635     if(Roo.isLinux){
6636         cls.push("roo-linux");
6637     }
6638     if(Roo.isIOS){
6639         cls.push("roo-ios");
6640     }
6641     if(Roo.isTouch){
6642         cls.push("roo-touch");
6643     }
6644     if(Roo.isBorderBox){
6645         cls.push('roo-border-box');
6646     }
6647     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6648         var p = bd.dom.parentNode;
6649         if(p){
6650             p.className += ' roo-strict';
6651         }
6652     }
6653     bd.addClass(cls.join(' '));
6654 });
6655
6656 /**
6657  * @class Roo.EventObject
6658  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6659  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6660  * Example:
6661  * <pre><code>
6662  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6663     e.preventDefault();
6664     var target = e.getTarget();
6665     ...
6666  }
6667  var myDiv = Roo.get("myDiv");
6668  myDiv.on("click", handleClick);
6669  //or
6670  Roo.EventManager.on("myDiv", 'click', handleClick);
6671  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6672  </code></pre>
6673  * @singleton
6674  */
6675 Roo.EventObject = function(){
6676     
6677     var E = Roo.lib.Event;
6678     
6679     // safari keypress events for special keys return bad keycodes
6680     var safariKeys = {
6681         63234 : 37, // left
6682         63235 : 39, // right
6683         63232 : 38, // up
6684         63233 : 40, // down
6685         63276 : 33, // page up
6686         63277 : 34, // page down
6687         63272 : 46, // delete
6688         63273 : 36, // home
6689         63275 : 35  // end
6690     };
6691
6692     // normalize button clicks
6693     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6694                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6695
6696     Roo.EventObjectImpl = function(e){
6697         if(e){
6698             this.setEvent(e.browserEvent || e);
6699         }
6700     };
6701     Roo.EventObjectImpl.prototype = {
6702         /**
6703          * Used to fix doc tools.
6704          * @scope Roo.EventObject.prototype
6705          */
6706             
6707
6708         
6709         
6710         /** The normal browser event */
6711         browserEvent : null,
6712         /** The button pressed in a mouse event */
6713         button : -1,
6714         /** True if the shift key was down during the event */
6715         shiftKey : false,
6716         /** True if the control key was down during the event */
6717         ctrlKey : false,
6718         /** True if the alt key was down during the event */
6719         altKey : false,
6720
6721         /** Key constant 
6722         * @type Number */
6723         BACKSPACE : 8,
6724         /** Key constant 
6725         * @type Number */
6726         TAB : 9,
6727         /** Key constant 
6728         * @type Number */
6729         RETURN : 13,
6730         /** Key constant 
6731         * @type Number */
6732         ENTER : 13,
6733         /** Key constant 
6734         * @type Number */
6735         SHIFT : 16,
6736         /** Key constant 
6737         * @type Number */
6738         CONTROL : 17,
6739         /** Key constant 
6740         * @type Number */
6741         ESC : 27,
6742         /** Key constant 
6743         * @type Number */
6744         SPACE : 32,
6745         /** Key constant 
6746         * @type Number */
6747         PAGEUP : 33,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEDOWN : 34,
6751         /** Key constant 
6752         * @type Number */
6753         END : 35,
6754         /** Key constant 
6755         * @type Number */
6756         HOME : 36,
6757         /** Key constant 
6758         * @type Number */
6759         LEFT : 37,
6760         /** Key constant 
6761         * @type Number */
6762         UP : 38,
6763         /** Key constant 
6764         * @type Number */
6765         RIGHT : 39,
6766         /** Key constant 
6767         * @type Number */
6768         DOWN : 40,
6769         /** Key constant 
6770         * @type Number */
6771         DELETE : 46,
6772         /** Key constant 
6773         * @type Number */
6774         F5 : 116,
6775
6776            /** @private */
6777         setEvent : function(e){
6778             if(e == this || (e && e.browserEvent)){ // already wrapped
6779                 return e;
6780             }
6781             this.browserEvent = e;
6782             if(e){
6783                 // normalize buttons
6784                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6785                 if(e.type == 'click' && this.button == -1){
6786                     this.button = 0;
6787                 }
6788                 this.type = e.type;
6789                 this.shiftKey = e.shiftKey;
6790                 // mac metaKey behaves like ctrlKey
6791                 this.ctrlKey = e.ctrlKey || e.metaKey;
6792                 this.altKey = e.altKey;
6793                 // in getKey these will be normalized for the mac
6794                 this.keyCode = e.keyCode;
6795                 // keyup warnings on firefox.
6796                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6797                 // cache the target for the delayed and or buffered events
6798                 this.target = E.getTarget(e);
6799                 // same for XY
6800                 this.xy = E.getXY(e);
6801             }else{
6802                 this.button = -1;
6803                 this.shiftKey = false;
6804                 this.ctrlKey = false;
6805                 this.altKey = false;
6806                 this.keyCode = 0;
6807                 this.charCode =0;
6808                 this.target = null;
6809                 this.xy = [0, 0];
6810             }
6811             return this;
6812         },
6813
6814         /**
6815          * Stop the event (preventDefault and stopPropagation)
6816          */
6817         stopEvent : function(){
6818             if(this.browserEvent){
6819                 if(this.browserEvent.type == 'mousedown'){
6820                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6821                 }
6822                 E.stopEvent(this.browserEvent);
6823             }
6824         },
6825
6826         /**
6827          * Prevents the browsers default handling of the event.
6828          */
6829         preventDefault : function(){
6830             if(this.browserEvent){
6831                 E.preventDefault(this.browserEvent);
6832             }
6833         },
6834
6835         /** @private */
6836         isNavKeyPress : function(){
6837             var k = this.keyCode;
6838             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6839             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6840         },
6841
6842         isSpecialKey : function(){
6843             var k = this.keyCode;
6844             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6845             (k == 16) || (k == 17) ||
6846             (k >= 18 && k <= 20) ||
6847             (k >= 33 && k <= 35) ||
6848             (k >= 36 && k <= 39) ||
6849             (k >= 44 && k <= 45);
6850         },
6851         /**
6852          * Cancels bubbling of the event.
6853          */
6854         stopPropagation : function(){
6855             if(this.browserEvent){
6856                 if(this.type == 'mousedown'){
6857                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6858                 }
6859                 E.stopPropagation(this.browserEvent);
6860             }
6861         },
6862
6863         /**
6864          * Gets the key code for the event.
6865          * @return {Number}
6866          */
6867         getCharCode : function(){
6868             return this.charCode || this.keyCode;
6869         },
6870
6871         /**
6872          * Returns a normalized keyCode for the event.
6873          * @return {Number} The key code
6874          */
6875         getKey : function(){
6876             var k = this.keyCode || this.charCode;
6877             return Roo.isSafari ? (safariKeys[k] || k) : k;
6878         },
6879
6880         /**
6881          * Gets the x coordinate of the event.
6882          * @return {Number}
6883          */
6884         getPageX : function(){
6885             return this.xy[0];
6886         },
6887
6888         /**
6889          * Gets the y coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageY : function(){
6893             return this.xy[1];
6894         },
6895
6896         /**
6897          * Gets the time of the event.
6898          * @return {Number}
6899          */
6900         getTime : function(){
6901             if(this.browserEvent){
6902                 return E.getTime(this.browserEvent);
6903             }
6904             return null;
6905         },
6906
6907         /**
6908          * Gets the page coordinates of the event.
6909          * @return {Array} The xy values like [x, y]
6910          */
6911         getXY : function(){
6912             return this.xy;
6913         },
6914
6915         /**
6916          * Gets the target for the event.
6917          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6918          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6919                 search as a number or element (defaults to 10 || document.body)
6920          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6921          * @return {HTMLelement}
6922          */
6923         getTarget : function(selector, maxDepth, returnEl){
6924             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6925         },
6926         /**
6927          * Gets the related target.
6928          * @return {HTMLElement}
6929          */
6930         getRelatedTarget : function(){
6931             if(this.browserEvent){
6932                 return E.getRelatedTarget(this.browserEvent);
6933             }
6934             return null;
6935         },
6936
6937         /**
6938          * Normalizes mouse wheel delta across browsers
6939          * @return {Number} The delta
6940          */
6941         getWheelDelta : function(){
6942             var e = this.browserEvent;
6943             var delta = 0;
6944             if(e.wheelDelta){ /* IE/Opera. */
6945                 delta = e.wheelDelta/120;
6946             }else if(e.detail){ /* Mozilla case. */
6947                 delta = -e.detail/3;
6948             }
6949             return delta;
6950         },
6951
6952         /**
6953          * Returns true if the control, meta, shift or alt key was pressed during this event.
6954          * @return {Boolean}
6955          */
6956         hasModifier : function(){
6957             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6958         },
6959
6960         /**
6961          * Returns true if the target of this event equals el or is a child of el
6962          * @param {String/HTMLElement/Element} el
6963          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6964          * @return {Boolean}
6965          */
6966         within : function(el, related){
6967             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6968             return t && Roo.fly(el).contains(t);
6969         },
6970
6971         getPoint : function(){
6972             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6973         }
6974     };
6975
6976     return new Roo.EventObjectImpl();
6977 }();
6978             
6979     /*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990  
6991 // was in Composite Element!??!?!
6992  
6993 (function(){
6994     var D = Roo.lib.Dom;
6995     var E = Roo.lib.Event;
6996     var A = Roo.lib.Anim;
6997
6998     // local style camelizing for speed
6999     var propCache = {};
7000     var camelRe = /(-[a-z])/gi;
7001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7002     var view = document.defaultView;
7003
7004 /**
7005  * @class Roo.Element
7006  * Represents an Element in the DOM.<br><br>
7007  * Usage:<br>
7008 <pre><code>
7009 var el = Roo.get("my-div");
7010
7011 // or with getEl
7012 var el = getEl("my-div");
7013
7014 // or with a DOM element
7015 var el = Roo.get(myDivElement);
7016 </code></pre>
7017  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7018  * each call instead of constructing a new one.<br><br>
7019  * <b>Animations</b><br />
7020  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7021  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7022 <pre>
7023 Option    Default   Description
7024 --------- --------  ---------------------------------------------
7025 duration  .35       The duration of the animation in seconds
7026 easing    easeOut   The YUI easing method
7027 callback  none      A function to execute when the anim completes
7028 scope     this      The scope (this) of the callback function
7029 </pre>
7030 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7031 * manipulate the animation. Here's an example:
7032 <pre><code>
7033 var el = Roo.get("my-div");
7034
7035 // no animation
7036 el.setWidth(100);
7037
7038 // default animation
7039 el.setWidth(100, true);
7040
7041 // animation with some options set
7042 el.setWidth(100, {
7043     duration: 1,
7044     callback: this.foo,
7045     scope: this
7046 });
7047
7048 // using the "anim" property to get the Anim object
7049 var opt = {
7050     duration: 1,
7051     callback: this.foo,
7052     scope: this
7053 };
7054 el.setWidth(100, opt);
7055 ...
7056 if(opt.anim.isAnimated()){
7057     opt.anim.stop();
7058 }
7059 </code></pre>
7060 * <b> Composite (Collections of) Elements</b><br />
7061  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7062  * @constructor Create a new Element directly.
7063  * @param {String/HTMLElement} element
7064  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7065  */
7066     Roo.Element = function(element, forceNew){
7067         var dom = typeof element == "string" ?
7068                 document.getElementById(element) : element;
7069         if(!dom){ // invalid id/element
7070             return null;
7071         }
7072         var id = dom.id;
7073         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7074             return Roo.Element.cache[id];
7075         }
7076
7077         /**
7078          * The DOM element
7079          * @type HTMLElement
7080          */
7081         this.dom = dom;
7082
7083         /**
7084          * The DOM element ID
7085          * @type String
7086          */
7087         this.id = id || Roo.id(dom);
7088     };
7089
7090     var El = Roo.Element;
7091
7092     El.prototype = {
7093         /**
7094          * The element's default display mode  (defaults to "")
7095          * @type String
7096          */
7097         originalDisplay : "",
7098
7099         visibilityMode : 1,
7100         /**
7101          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7102          * @type String
7103          */
7104         defaultUnit : "px",
7105         
7106         /**
7107          * Sets the element's visibility mode. When setVisible() is called it
7108          * will use this to determine whether to set the visibility or the display property.
7109          * @param visMode Element.VISIBILITY or Element.DISPLAY
7110          * @return {Roo.Element} this
7111          */
7112         setVisibilityMode : function(visMode){
7113             this.visibilityMode = visMode;
7114             return this;
7115         },
7116         /**
7117          * Convenience method for setVisibilityMode(Element.DISPLAY)
7118          * @param {String} display (optional) What to set display to when visible
7119          * @return {Roo.Element} this
7120          */
7121         enableDisplayMode : function(display){
7122             this.setVisibilityMode(El.DISPLAY);
7123             if(typeof display != "undefined") { this.originalDisplay = display; }
7124             return this;
7125         },
7126
7127         /**
7128          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7129          * @param {String} selector The simple selector to test
7130          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7131                 search as a number or element (defaults to 10 || document.body)
7132          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7133          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7134          */
7135         findParent : function(simpleSelector, maxDepth, returnEl){
7136             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7137             maxDepth = maxDepth || 50;
7138             if(typeof maxDepth != "number"){
7139                 stopEl = Roo.getDom(maxDepth);
7140                 maxDepth = 10;
7141             }
7142             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7143                 if(dq.is(p, simpleSelector)){
7144                     return returnEl ? Roo.get(p) : p;
7145                 }
7146                 depth++;
7147                 p = p.parentNode;
7148             }
7149             return null;
7150         },
7151
7152
7153         /**
7154          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7155          * @param {String} selector The simple selector to test
7156          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7157                 search as a number or element (defaults to 10 || document.body)
7158          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7159          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7160          */
7161         findParentNode : function(simpleSelector, maxDepth, returnEl){
7162             var p = Roo.fly(this.dom.parentNode, '_internal');
7163             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7164         },
7165         
7166         /**
7167          * Looks at  the scrollable parent element
7168          */
7169         findScrollableParent : function(){
7170             
7171             var overflowRegex = /(auto|scroll)/;
7172             
7173             if(this.getStyle('position') === 'fixed'){
7174                 return Roo.get(document.body);
7175             }
7176             
7177             var excludeStaticParent = this.getStyle('position') === "absolute";
7178             
7179             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7180                 
7181                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7182                     continue;
7183                 }
7184                 
7185                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7186                     return parent;
7187                 }
7188             }
7189             
7190             return Roo.get(document.body);
7191         },
7192
7193         /**
7194          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7195          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7196          * @param {String} selector The simple selector to test
7197          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7198                 search as a number or element (defaults to 10 || document.body)
7199          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7200          */
7201         up : function(simpleSelector, maxDepth){
7202             return this.findParentNode(simpleSelector, maxDepth, true);
7203         },
7204
7205
7206
7207         /**
7208          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7209          * @param {String} selector The simple selector to test
7210          * @return {Boolean} True if this element matches the selector, else false
7211          */
7212         is : function(simpleSelector){
7213             return Roo.DomQuery.is(this.dom, simpleSelector);
7214         },
7215
7216         /**
7217          * Perform animation on this element.
7218          * @param {Object} args The YUI animation control args
7219          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7220          * @param {Function} onComplete (optional) Function to call when animation completes
7221          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7222          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7223          * @return {Roo.Element} this
7224          */
7225         animate : function(args, duration, onComplete, easing, animType){
7226             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7227             return this;
7228         },
7229
7230         /*
7231          * @private Internal animation call
7232          */
7233         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7234             animType = animType || 'run';
7235             opt = opt || {};
7236             var anim = Roo.lib.Anim[animType](
7237                 this.dom, args,
7238                 (opt.duration || defaultDur) || .35,
7239                 (opt.easing || defaultEase) || 'easeOut',
7240                 function(){
7241                     Roo.callback(cb, this);
7242                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7243                 },
7244                 this
7245             );
7246             opt.anim = anim;
7247             return anim;
7248         },
7249
7250         // private legacy anim prep
7251         preanim : function(a, i){
7252             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7253         },
7254
7255         /**
7256          * Removes worthless text nodes
7257          * @param {Boolean} forceReclean (optional) By default the element
7258          * keeps track if it has been cleaned already so
7259          * you can call this over and over. However, if you update the element and
7260          * need to force a reclean, you can pass true.
7261          */
7262         clean : function(forceReclean){
7263             if(this.isCleaned && forceReclean !== true){
7264                 return this;
7265             }
7266             var ns = /\S/;
7267             var d = this.dom, n = d.firstChild, ni = -1;
7268             while(n){
7269                 var nx = n.nextSibling;
7270                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7271                     d.removeChild(n);
7272                 }else{
7273                     n.nodeIndex = ++ni;
7274                 }
7275                 n = nx;
7276             }
7277             this.isCleaned = true;
7278             return this;
7279         },
7280
7281         // private
7282         calcOffsetsTo : function(el){
7283             el = Roo.get(el);
7284             var d = el.dom;
7285             var restorePos = false;
7286             if(el.getStyle('position') == 'static'){
7287                 el.position('relative');
7288                 restorePos = true;
7289             }
7290             var x = 0, y =0;
7291             var op = this.dom;
7292             while(op && op != d && op.tagName != 'HTML'){
7293                 x+= op.offsetLeft;
7294                 y+= op.offsetTop;
7295                 op = op.offsetParent;
7296             }
7297             if(restorePos){
7298                 el.position('static');
7299             }
7300             return [x, y];
7301         },
7302
7303         /**
7304          * Scrolls this element into view within the passed container.
7305          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7306          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7307          * @return {Roo.Element} this
7308          */
7309         scrollIntoView : function(container, hscroll){
7310             var c = Roo.getDom(container) || document.body;
7311             var el = this.dom;
7312
7313             var o = this.calcOffsetsTo(c),
7314                 l = o[0],
7315                 t = o[1],
7316                 b = t+el.offsetHeight,
7317                 r = l+el.offsetWidth;
7318
7319             var ch = c.clientHeight;
7320             var ct = parseInt(c.scrollTop, 10);
7321             var cl = parseInt(c.scrollLeft, 10);
7322             var cb = ct + ch;
7323             var cr = cl + c.clientWidth;
7324
7325             if(t < ct){
7326                 c.scrollTop = t;
7327             }else if(b > cb){
7328                 c.scrollTop = b-ch;
7329             }
7330
7331             if(hscroll !== false){
7332                 if(l < cl){
7333                     c.scrollLeft = l;
7334                 }else if(r > cr){
7335                     c.scrollLeft = r-c.clientWidth;
7336                 }
7337             }
7338             return this;
7339         },
7340
7341         // private
7342         scrollChildIntoView : function(child, hscroll){
7343             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7344         },
7345
7346         /**
7347          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7348          * the new height may not be available immediately.
7349          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7350          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7351          * @param {Function} onComplete (optional) Function to call when animation completes
7352          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7353          * @return {Roo.Element} this
7354          */
7355         autoHeight : function(animate, duration, onComplete, easing){
7356             var oldHeight = this.getHeight();
7357             this.clip();
7358             this.setHeight(1); // force clipping
7359             setTimeout(function(){
7360                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7361                 if(!animate){
7362                     this.setHeight(height);
7363                     this.unclip();
7364                     if(typeof onComplete == "function"){
7365                         onComplete();
7366                     }
7367                 }else{
7368                     this.setHeight(oldHeight); // restore original height
7369                     this.setHeight(height, animate, duration, function(){
7370                         this.unclip();
7371                         if(typeof onComplete == "function") { onComplete(); }
7372                     }.createDelegate(this), easing);
7373                 }
7374             }.createDelegate(this), 0);
7375             return this;
7376         },
7377
7378         /**
7379          * Returns true if this element is an ancestor of the passed element
7380          * @param {HTMLElement/String} el The element to check
7381          * @return {Boolean} True if this element is an ancestor of el, else false
7382          */
7383         contains : function(el){
7384             if(!el){return false;}
7385             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7386         },
7387
7388         /**
7389          * Checks whether the element is currently visible using both visibility and display properties.
7390          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7391          * @return {Boolean} True if the element is currently visible, else false
7392          */
7393         isVisible : function(deep) {
7394             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7395             if(deep !== true || !vis){
7396                 return vis;
7397             }
7398             var p = this.dom.parentNode;
7399             while(p && p.tagName.toLowerCase() != "body"){
7400                 if(!Roo.fly(p, '_isVisible').isVisible()){
7401                     return false;
7402                 }
7403                 p = p.parentNode;
7404             }
7405             return true;
7406         },
7407
7408         /**
7409          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7410          * @param {String} selector The CSS selector
7411          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7412          * @return {CompositeElement/CompositeElementLite} The composite element
7413          */
7414         select : function(selector, unique){
7415             return El.select(selector, unique, this.dom);
7416         },
7417
7418         /**
7419          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7420          * @param {String} selector The CSS selector
7421          * @return {Array} An array of the matched nodes
7422          */
7423         query : function(selector, unique){
7424             return Roo.DomQuery.select(selector, this.dom);
7425         },
7426
7427         /**
7428          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7429          * @param {String} selector The CSS selector
7430          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7431          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7432          */
7433         child : function(selector, returnDom){
7434             var n = Roo.DomQuery.selectNode(selector, this.dom);
7435             return returnDom ? n : Roo.get(n);
7436         },
7437
7438         /**
7439          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7440          * @param {String} selector The CSS selector
7441          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7442          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7443          */
7444         down : function(selector, returnDom){
7445             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7446             return returnDom ? n : Roo.get(n);
7447         },
7448
7449         /**
7450          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7451          * @param {String} group The group the DD object is member of
7452          * @param {Object} config The DD config object
7453          * @param {Object} overrides An object containing methods to override/implement on the DD object
7454          * @return {Roo.dd.DD} The DD object
7455          */
7456         initDD : function(group, config, overrides){
7457             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7458             return Roo.apply(dd, overrides);
7459         },
7460
7461         /**
7462          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7463          * @param {String} group The group the DDProxy object is member of
7464          * @param {Object} config The DDProxy config object
7465          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7466          * @return {Roo.dd.DDProxy} The DDProxy object
7467          */
7468         initDDProxy : function(group, config, overrides){
7469             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7470             return Roo.apply(dd, overrides);
7471         },
7472
7473         /**
7474          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7475          * @param {String} group The group the DDTarget object is member of
7476          * @param {Object} config The DDTarget config object
7477          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7478          * @return {Roo.dd.DDTarget} The DDTarget object
7479          */
7480         initDDTarget : function(group, config, overrides){
7481             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7482             return Roo.apply(dd, overrides);
7483         },
7484
7485         /**
7486          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7487          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7488          * @param {Boolean} visible Whether the element is visible
7489          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7490          * @return {Roo.Element} this
7491          */
7492          setVisible : function(visible, animate){
7493             if(!animate || !A){
7494                 if(this.visibilityMode == El.DISPLAY){
7495                     this.setDisplayed(visible);
7496                 }else{
7497                     this.fixDisplay();
7498                     this.dom.style.visibility = visible ? "visible" : "hidden";
7499                 }
7500             }else{
7501                 // closure for composites
7502                 var dom = this.dom;
7503                 var visMode = this.visibilityMode;
7504                 if(visible){
7505                     this.setOpacity(.01);
7506                     this.setVisible(true);
7507                 }
7508                 this.anim({opacity: { to: (visible?1:0) }},
7509                       this.preanim(arguments, 1),
7510                       null, .35, 'easeIn', function(){
7511                          if(!visible){
7512                              if(visMode == El.DISPLAY){
7513                                  dom.style.display = "none";
7514                              }else{
7515                                  dom.style.visibility = "hidden";
7516                              }
7517                              Roo.get(dom).setOpacity(1);
7518                          }
7519                      });
7520             }
7521             return this;
7522         },
7523
7524         /**
7525          * Returns true if display is not "none"
7526          * @return {Boolean}
7527          */
7528         isDisplayed : function() {
7529             return this.getStyle("display") != "none";
7530         },
7531
7532         /**
7533          * Toggles the element's visibility or display, depending on visibility mode.
7534          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7535          * @return {Roo.Element} this
7536          */
7537         toggle : function(animate){
7538             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7539             return this;
7540         },
7541
7542         /**
7543          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7544          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7545          * @return {Roo.Element} this
7546          */
7547         setDisplayed : function(value) {
7548             if(typeof value == "boolean"){
7549                value = value ? this.originalDisplay : "none";
7550             }
7551             this.setStyle("display", value);
7552             return this;
7553         },
7554
7555         /**
7556          * Tries to focus the element. Any exceptions are caught and ignored.
7557          * @return {Roo.Element} this
7558          */
7559         focus : function() {
7560             try{
7561                 this.dom.focus();
7562             }catch(e){}
7563             return this;
7564         },
7565
7566         /**
7567          * Tries to blur the element. Any exceptions are caught and ignored.
7568          * @return {Roo.Element} this
7569          */
7570         blur : function() {
7571             try{
7572                 this.dom.blur();
7573             }catch(e){}
7574             return this;
7575         },
7576
7577         /**
7578          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7579          * @param {String/Array} className The CSS class to add, or an array of classes
7580          * @return {Roo.Element} this
7581          */
7582         addClass : function(className){
7583             if(className instanceof Array){
7584                 for(var i = 0, len = className.length; i < len; i++) {
7585                     this.addClass(className[i]);
7586                 }
7587             }else{
7588                 if(className && !this.hasClass(className)){
7589                     this.dom.className = this.dom.className + " " + className;
7590                 }
7591             }
7592             return this;
7593         },
7594
7595         /**
7596          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7597          * @param {String/Array} className The CSS class to add, or an array of classes
7598          * @return {Roo.Element} this
7599          */
7600         radioClass : function(className){
7601             var siblings = this.dom.parentNode.childNodes;
7602             for(var i = 0; i < siblings.length; i++) {
7603                 var s = siblings[i];
7604                 if(s.nodeType == 1){
7605                     Roo.get(s).removeClass(className);
7606                 }
7607             }
7608             this.addClass(className);
7609             return this;
7610         },
7611
7612         /**
7613          * Removes one or more CSS classes from the element.
7614          * @param {String/Array} className The CSS class to remove, or an array of classes
7615          * @return {Roo.Element} this
7616          */
7617         removeClass : function(className){
7618             if(!className || !this.dom.className){
7619                 return this;
7620             }
7621             if(className instanceof Array){
7622                 for(var i = 0, len = className.length; i < len; i++) {
7623                     this.removeClass(className[i]);
7624                 }
7625             }else{
7626                 if(this.hasClass(className)){
7627                     var re = this.classReCache[className];
7628                     if (!re) {
7629                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7630                        this.classReCache[className] = re;
7631                     }
7632                     this.dom.className =
7633                         this.dom.className.replace(re, " ");
7634                 }
7635             }
7636             return this;
7637         },
7638
7639         // private
7640         classReCache: {},
7641
7642         /**
7643          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7644          * @param {String} className The CSS class to toggle
7645          * @return {Roo.Element} this
7646          */
7647         toggleClass : function(className){
7648             if(this.hasClass(className)){
7649                 this.removeClass(className);
7650             }else{
7651                 this.addClass(className);
7652             }
7653             return this;
7654         },
7655
7656         /**
7657          * Checks if the specified CSS class exists on this element's DOM node.
7658          * @param {String} className The CSS class to check for
7659          * @return {Boolean} True if the class exists, else false
7660          */
7661         hasClass : function(className){
7662             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7663         },
7664
7665         /**
7666          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7667          * @param {String} oldClassName The CSS class to replace
7668          * @param {String} newClassName The replacement CSS class
7669          * @return {Roo.Element} this
7670          */
7671         replaceClass : function(oldClassName, newClassName){
7672             this.removeClass(oldClassName);
7673             this.addClass(newClassName);
7674             return this;
7675         },
7676
7677         /**
7678          * Returns an object with properties matching the styles requested.
7679          * For example, el.getStyles('color', 'font-size', 'width') might return
7680          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7681          * @param {String} style1 A style name
7682          * @param {String} style2 A style name
7683          * @param {String} etc.
7684          * @return {Object} The style object
7685          */
7686         getStyles : function(){
7687             var a = arguments, len = a.length, r = {};
7688             for(var i = 0; i < len; i++){
7689                 r[a[i]] = this.getStyle(a[i]);
7690             }
7691             return r;
7692         },
7693
7694         /**
7695          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7696          * @param {String} property The style property whose value is returned.
7697          * @return {String} The current value of the style property for this element.
7698          */
7699         getStyle : function(){
7700             return view && view.getComputedStyle ?
7701                 function(prop){
7702                     var el = this.dom, v, cs, camel;
7703                     if(prop == 'float'){
7704                         prop = "cssFloat";
7705                     }
7706                     if(el.style && (v = el.style[prop])){
7707                         return v;
7708                     }
7709                     if(cs = view.getComputedStyle(el, "")){
7710                         if(!(camel = propCache[prop])){
7711                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7712                         }
7713                         return cs[camel];
7714                     }
7715                     return null;
7716                 } :
7717                 function(prop){
7718                     var el = this.dom, v, cs, camel;
7719                     if(prop == 'opacity'){
7720                         if(typeof el.style.filter == 'string'){
7721                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7722                             if(m){
7723                                 var fv = parseFloat(m[1]);
7724                                 if(!isNaN(fv)){
7725                                     return fv ? fv / 100 : 0;
7726                                 }
7727                             }
7728                         }
7729                         return 1;
7730                     }else if(prop == 'float'){
7731                         prop = "styleFloat";
7732                     }
7733                     if(!(camel = propCache[prop])){
7734                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7735                     }
7736                     if(v = el.style[camel]){
7737                         return v;
7738                     }
7739                     if(cs = el.currentStyle){
7740                         return cs[camel];
7741                     }
7742                     return null;
7743                 };
7744         }(),
7745
7746         /**
7747          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7748          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7749          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7750          * @return {Roo.Element} this
7751          */
7752         setStyle : function(prop, value){
7753             if(typeof prop == "string"){
7754                 
7755                 if (prop == 'float') {
7756                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7757                     return this;
7758                 }
7759                 
7760                 var camel;
7761                 if(!(camel = propCache[prop])){
7762                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7763                 }
7764                 
7765                 if(camel == 'opacity') {
7766                     this.setOpacity(value);
7767                 }else{
7768                     this.dom.style[camel] = value;
7769                 }
7770             }else{
7771                 for(var style in prop){
7772                     if(typeof prop[style] != "function"){
7773                        this.setStyle(style, prop[style]);
7774                     }
7775                 }
7776             }
7777             return this;
7778         },
7779
7780         /**
7781          * More flexible version of {@link #setStyle} for setting style properties.
7782          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7783          * a function which returns such a specification.
7784          * @return {Roo.Element} this
7785          */
7786         applyStyles : function(style){
7787             Roo.DomHelper.applyStyles(this.dom, style);
7788             return this;
7789         },
7790
7791         /**
7792           * 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).
7793           * @return {Number} The X position of the element
7794           */
7795         getX : function(){
7796             return D.getX(this.dom);
7797         },
7798
7799         /**
7800           * 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).
7801           * @return {Number} The Y position of the element
7802           */
7803         getY : function(){
7804             return D.getY(this.dom);
7805         },
7806
7807         /**
7808           * 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).
7809           * @return {Array} The XY position of the element
7810           */
7811         getXY : function(){
7812             return D.getXY(this.dom);
7813         },
7814
7815         /**
7816          * 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).
7817          * @param {Number} The X position of the element
7818          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7819          * @return {Roo.Element} this
7820          */
7821         setX : function(x, animate){
7822             if(!animate || !A){
7823                 D.setX(this.dom, x);
7824             }else{
7825                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7826             }
7827             return this;
7828         },
7829
7830         /**
7831          * 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).
7832          * @param {Number} The Y position of the element
7833          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7834          * @return {Roo.Element} this
7835          */
7836         setY : function(y, animate){
7837             if(!animate || !A){
7838                 D.setY(this.dom, y);
7839             }else{
7840                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7841             }
7842             return this;
7843         },
7844
7845         /**
7846          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7847          * @param {String} left The left CSS property value
7848          * @return {Roo.Element} this
7849          */
7850         setLeft : function(left){
7851             this.setStyle("left", this.addUnits(left));
7852             return this;
7853         },
7854
7855         /**
7856          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7857          * @param {String} top The top CSS property value
7858          * @return {Roo.Element} this
7859          */
7860         setTop : function(top){
7861             this.setStyle("top", this.addUnits(top));
7862             return this;
7863         },
7864
7865         /**
7866          * Sets the element's CSS right style.
7867          * @param {String} right The right CSS property value
7868          * @return {Roo.Element} this
7869          */
7870         setRight : function(right){
7871             this.setStyle("right", this.addUnits(right));
7872             return this;
7873         },
7874
7875         /**
7876          * Sets the element's CSS bottom style.
7877          * @param {String} bottom The bottom CSS property value
7878          * @return {Roo.Element} this
7879          */
7880         setBottom : function(bottom){
7881             this.setStyle("bottom", this.addUnits(bottom));
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7887          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7888          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7889          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7890          * @return {Roo.Element} this
7891          */
7892         setXY : function(pos, animate){
7893             if(!animate || !A){
7894                 D.setXY(this.dom, pos);
7895             }else{
7896                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7897             }
7898             return this;
7899         },
7900
7901         /**
7902          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7903          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7904          * @param {Number} x X value for new position (coordinates are page-based)
7905          * @param {Number} y Y value for new position (coordinates are page-based)
7906          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7907          * @return {Roo.Element} this
7908          */
7909         setLocation : function(x, y, animate){
7910             this.setXY([x, y], this.preanim(arguments, 2));
7911             return this;
7912         },
7913
7914         /**
7915          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7916          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7917          * @param {Number} x X value for new position (coordinates are page-based)
7918          * @param {Number} y Y value for new position (coordinates are page-based)
7919          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7920          * @return {Roo.Element} this
7921          */
7922         moveTo : function(x, y, animate){
7923             this.setXY([x, y], this.preanim(arguments, 2));
7924             return this;
7925         },
7926
7927         /**
7928          * Returns the region of the given element.
7929          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7930          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7931          */
7932         getRegion : function(){
7933             return D.getRegion(this.dom);
7934         },
7935
7936         /**
7937          * Returns the offset height of the element
7938          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7939          * @return {Number} The element's height
7940          */
7941         getHeight : function(contentHeight){
7942             var h = this.dom.offsetHeight || 0;
7943             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7944         },
7945
7946         /**
7947          * Returns the offset width of the element
7948          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7949          * @return {Number} The element's width
7950          */
7951         getWidth : function(contentWidth){
7952             var w = this.dom.offsetWidth || 0;
7953             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7954         },
7955
7956         /**
7957          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7958          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7959          * if a height has not been set using CSS.
7960          * @return {Number}
7961          */
7962         getComputedHeight : function(){
7963             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7964             if(!h){
7965                 h = parseInt(this.getStyle('height'), 10) || 0;
7966                 if(!this.isBorderBox()){
7967                     h += this.getFrameWidth('tb');
7968                 }
7969             }
7970             return h;
7971         },
7972
7973         /**
7974          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7975          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7976          * if a width has not been set using CSS.
7977          * @return {Number}
7978          */
7979         getComputedWidth : function(){
7980             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7981             if(!w){
7982                 w = parseInt(this.getStyle('width'), 10) || 0;
7983                 if(!this.isBorderBox()){
7984                     w += this.getFrameWidth('lr');
7985                 }
7986             }
7987             return w;
7988         },
7989
7990         /**
7991          * Returns the size of the element.
7992          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7993          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7994          */
7995         getSize : function(contentSize){
7996             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7997         },
7998
7999         /**
8000          * Returns the width and height of the viewport.
8001          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8002          */
8003         getViewSize : function(){
8004             var d = this.dom, doc = document, aw = 0, ah = 0;
8005             if(d == doc || d == doc.body){
8006                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8007             }else{
8008                 return {
8009                     width : d.clientWidth,
8010                     height: d.clientHeight
8011                 };
8012             }
8013         },
8014
8015         /**
8016          * Returns the value of the "value" attribute
8017          * @param {Boolean} asNumber true to parse the value as a number
8018          * @return {String/Number}
8019          */
8020         getValue : function(asNumber){
8021             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8022         },
8023
8024         // private
8025         adjustWidth : function(width){
8026             if(typeof width == "number"){
8027                 if(this.autoBoxAdjust && !this.isBorderBox()){
8028                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8029                 }
8030                 if(width < 0){
8031                     width = 0;
8032                 }
8033             }
8034             return width;
8035         },
8036
8037         // private
8038         adjustHeight : function(height){
8039             if(typeof height == "number"){
8040                if(this.autoBoxAdjust && !this.isBorderBox()){
8041                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8042                }
8043                if(height < 0){
8044                    height = 0;
8045                }
8046             }
8047             return height;
8048         },
8049
8050         /**
8051          * Set the width of the element
8052          * @param {Number} width The new width
8053          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8054          * @return {Roo.Element} this
8055          */
8056         setWidth : function(width, animate){
8057             width = this.adjustWidth(width);
8058             if(!animate || !A){
8059                 this.dom.style.width = this.addUnits(width);
8060             }else{
8061                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8062             }
8063             return this;
8064         },
8065
8066         /**
8067          * Set the height of the element
8068          * @param {Number} height The new height
8069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8070          * @return {Roo.Element} this
8071          */
8072          setHeight : function(height, animate){
8073             height = this.adjustHeight(height);
8074             if(!animate || !A){
8075                 this.dom.style.height = this.addUnits(height);
8076             }else{
8077                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8078             }
8079             return this;
8080         },
8081
8082         /**
8083          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8084          * @param {Number} width The new width
8085          * @param {Number} height The new height
8086          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8087          * @return {Roo.Element} this
8088          */
8089          setSize : function(width, height, animate){
8090             if(typeof width == "object"){ // in case of object from getSize()
8091                 height = width.height; width = width.width;
8092             }
8093             width = this.adjustWidth(width); height = this.adjustHeight(height);
8094             if(!animate || !A){
8095                 this.dom.style.width = this.addUnits(width);
8096                 this.dom.style.height = this.addUnits(height);
8097             }else{
8098                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8099             }
8100             return this;
8101         },
8102
8103         /**
8104          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8105          * @param {Number} x X value for new position (coordinates are page-based)
8106          * @param {Number} y Y value for new position (coordinates are page-based)
8107          * @param {Number} width The new width
8108          * @param {Number} height The new height
8109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8110          * @return {Roo.Element} this
8111          */
8112         setBounds : function(x, y, width, height, animate){
8113             if(!animate || !A){
8114                 this.setSize(width, height);
8115                 this.setLocation(x, y);
8116             }else{
8117                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8118                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8119                               this.preanim(arguments, 4), 'motion');
8120             }
8121             return this;
8122         },
8123
8124         /**
8125          * 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.
8126          * @param {Roo.lib.Region} region The region to fill
8127          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8128          * @return {Roo.Element} this
8129          */
8130         setRegion : function(region, animate){
8131             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8132             return this;
8133         },
8134
8135         /**
8136          * Appends an event handler
8137          *
8138          * @param {String}   eventName     The type of event to append
8139          * @param {Function} fn        The method the event invokes
8140          * @param {Object} scope       (optional) The scope (this object) of the fn
8141          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8142          */
8143         addListener : function(eventName, fn, scope, options){
8144             if (this.dom) {
8145                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8146             }
8147         },
8148
8149         /**
8150          * Removes an event handler from this element
8151          * @param {String} eventName the type of event to remove
8152          * @param {Function} fn the method the event invokes
8153          * @return {Roo.Element} this
8154          */
8155         removeListener : function(eventName, fn){
8156             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8157             return this;
8158         },
8159
8160         /**
8161          * Removes all previous added listeners from this element
8162          * @return {Roo.Element} this
8163          */
8164         removeAllListeners : function(){
8165             E.purgeElement(this.dom);
8166             return this;
8167         },
8168
8169         relayEvent : function(eventName, observable){
8170             this.on(eventName, function(e){
8171                 observable.fireEvent(eventName, e);
8172             });
8173         },
8174
8175         /**
8176          * Set the opacity of the element
8177          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8178          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8179          * @return {Roo.Element} this
8180          */
8181          setOpacity : function(opacity, animate){
8182             if(!animate || !A){
8183                 var s = this.dom.style;
8184                 if(Roo.isIE){
8185                     s.zoom = 1;
8186                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8187                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8188                 }else{
8189                     s.opacity = opacity;
8190                 }
8191             }else{
8192                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8193             }
8194             return this;
8195         },
8196
8197         /**
8198          * Gets the left X coordinate
8199          * @param {Boolean} local True to get the local css position instead of page coordinate
8200          * @return {Number}
8201          */
8202         getLeft : function(local){
8203             if(!local){
8204                 return this.getX();
8205             }else{
8206                 return parseInt(this.getStyle("left"), 10) || 0;
8207             }
8208         },
8209
8210         /**
8211          * Gets the right X coordinate of the element (element X position + element width)
8212          * @param {Boolean} local True to get the local css position instead of page coordinate
8213          * @return {Number}
8214          */
8215         getRight : function(local){
8216             if(!local){
8217                 return this.getX() + this.getWidth();
8218             }else{
8219                 return (this.getLeft(true) + this.getWidth()) || 0;
8220             }
8221         },
8222
8223         /**
8224          * Gets the top Y coordinate
8225          * @param {Boolean} local True to get the local css position instead of page coordinate
8226          * @return {Number}
8227          */
8228         getTop : function(local) {
8229             if(!local){
8230                 return this.getY();
8231             }else{
8232                 return parseInt(this.getStyle("top"), 10) || 0;
8233             }
8234         },
8235
8236         /**
8237          * Gets the bottom Y coordinate of the element (element Y position + element height)
8238          * @param {Boolean} local True to get the local css position instead of page coordinate
8239          * @return {Number}
8240          */
8241         getBottom : function(local){
8242             if(!local){
8243                 return this.getY() + this.getHeight();
8244             }else{
8245                 return (this.getTop(true) + this.getHeight()) || 0;
8246             }
8247         },
8248
8249         /**
8250         * Initializes positioning on this element. If a desired position is not passed, it will make the
8251         * the element positioned relative IF it is not already positioned.
8252         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8253         * @param {Number} zIndex (optional) The zIndex to apply
8254         * @param {Number} x (optional) Set the page X position
8255         * @param {Number} y (optional) Set the page Y position
8256         */
8257         position : function(pos, zIndex, x, y){
8258             if(!pos){
8259                if(this.getStyle('position') == 'static'){
8260                    this.setStyle('position', 'relative');
8261                }
8262             }else{
8263                 this.setStyle("position", pos);
8264             }
8265             if(zIndex){
8266                 this.setStyle("z-index", zIndex);
8267             }
8268             if(x !== undefined && y !== undefined){
8269                 this.setXY([x, y]);
8270             }else if(x !== undefined){
8271                 this.setX(x);
8272             }else if(y !== undefined){
8273                 this.setY(y);
8274             }
8275         },
8276
8277         /**
8278         * Clear positioning back to the default when the document was loaded
8279         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8280         * @return {Roo.Element} this
8281          */
8282         clearPositioning : function(value){
8283             value = value ||'';
8284             this.setStyle({
8285                 "left": value,
8286                 "right": value,
8287                 "top": value,
8288                 "bottom": value,
8289                 "z-index": "",
8290                 "position" : "static"
8291             });
8292             return this;
8293         },
8294
8295         /**
8296         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8297         * snapshot before performing an update and then restoring the element.
8298         * @return {Object}
8299         */
8300         getPositioning : function(){
8301             var l = this.getStyle("left");
8302             var t = this.getStyle("top");
8303             return {
8304                 "position" : this.getStyle("position"),
8305                 "left" : l,
8306                 "right" : l ? "" : this.getStyle("right"),
8307                 "top" : t,
8308                 "bottom" : t ? "" : this.getStyle("bottom"),
8309                 "z-index" : this.getStyle("z-index")
8310             };
8311         },
8312
8313         /**
8314          * Gets the width of the border(s) for the specified side(s)
8315          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8316          * passing lr would get the border (l)eft width + the border (r)ight width.
8317          * @return {Number} The width of the sides passed added together
8318          */
8319         getBorderWidth : function(side){
8320             return this.addStyles(side, El.borders);
8321         },
8322
8323         /**
8324          * Gets the width of the padding(s) for the specified side(s)
8325          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8326          * passing lr would get the padding (l)eft + the padding (r)ight.
8327          * @return {Number} The padding of the sides passed added together
8328          */
8329         getPadding : function(side){
8330             return this.addStyles(side, El.paddings);
8331         },
8332
8333         /**
8334         * Set positioning with an object returned by getPositioning().
8335         * @param {Object} posCfg
8336         * @return {Roo.Element} this
8337          */
8338         setPositioning : function(pc){
8339             this.applyStyles(pc);
8340             if(pc.right == "auto"){
8341                 this.dom.style.right = "";
8342             }
8343             if(pc.bottom == "auto"){
8344                 this.dom.style.bottom = "";
8345             }
8346             return this;
8347         },
8348
8349         // private
8350         fixDisplay : function(){
8351             if(this.getStyle("display") == "none"){
8352                 this.setStyle("visibility", "hidden");
8353                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8354                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8355                     this.setStyle("display", "block");
8356                 }
8357             }
8358         },
8359
8360         /**
8361          * Quick set left and top adding default units
8362          * @param {String} left The left CSS property value
8363          * @param {String} top The top CSS property value
8364          * @return {Roo.Element} this
8365          */
8366          setLeftTop : function(left, top){
8367             this.dom.style.left = this.addUnits(left);
8368             this.dom.style.top = this.addUnits(top);
8369             return this;
8370         },
8371
8372         /**
8373          * Move this element relative to its current position.
8374          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8375          * @param {Number} distance How far to move the element in pixels
8376          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8377          * @return {Roo.Element} this
8378          */
8379          move : function(direction, distance, animate){
8380             var xy = this.getXY();
8381             direction = direction.toLowerCase();
8382             switch(direction){
8383                 case "l":
8384                 case "left":
8385                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8386                     break;
8387                case "r":
8388                case "right":
8389                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8390                     break;
8391                case "t":
8392                case "top":
8393                case "up":
8394                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8395                     break;
8396                case "b":
8397                case "bottom":
8398                case "down":
8399                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8400                     break;
8401             }
8402             return this;
8403         },
8404
8405         /**
8406          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8407          * @return {Roo.Element} this
8408          */
8409         clip : function(){
8410             if(!this.isClipped){
8411                this.isClipped = true;
8412                this.originalClip = {
8413                    "o": this.getStyle("overflow"),
8414                    "x": this.getStyle("overflow-x"),
8415                    "y": this.getStyle("overflow-y")
8416                };
8417                this.setStyle("overflow", "hidden");
8418                this.setStyle("overflow-x", "hidden");
8419                this.setStyle("overflow-y", "hidden");
8420             }
8421             return this;
8422         },
8423
8424         /**
8425          *  Return clipping (overflow) to original clipping before clip() was called
8426          * @return {Roo.Element} this
8427          */
8428         unclip : function(){
8429             if(this.isClipped){
8430                 this.isClipped = false;
8431                 var o = this.originalClip;
8432                 if(o.o){this.setStyle("overflow", o.o);}
8433                 if(o.x){this.setStyle("overflow-x", o.x);}
8434                 if(o.y){this.setStyle("overflow-y", o.y);}
8435             }
8436             return this;
8437         },
8438
8439
8440         /**
8441          * Gets the x,y coordinates specified by the anchor position on the element.
8442          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8443          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8444          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8445          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8446          * @return {Array} [x, y] An array containing the element's x and y coordinates
8447          */
8448         getAnchorXY : function(anchor, local, s){
8449             //Passing a different size is useful for pre-calculating anchors,
8450             //especially for anchored animations that change the el size.
8451
8452             var w, h, vp = false;
8453             if(!s){
8454                 var d = this.dom;
8455                 if(d == document.body || d == document){
8456                     vp = true;
8457                     w = D.getViewWidth(); h = D.getViewHeight();
8458                 }else{
8459                     w = this.getWidth(); h = this.getHeight();
8460                 }
8461             }else{
8462                 w = s.width;  h = s.height;
8463             }
8464             var x = 0, y = 0, r = Math.round;
8465             switch((anchor || "tl").toLowerCase()){
8466                 case "c":
8467                     x = r(w*.5);
8468                     y = r(h*.5);
8469                 break;
8470                 case "t":
8471                     x = r(w*.5);
8472                     y = 0;
8473                 break;
8474                 case "l":
8475                     x = 0;
8476                     y = r(h*.5);
8477                 break;
8478                 case "r":
8479                     x = w;
8480                     y = r(h*.5);
8481                 break;
8482                 case "b":
8483                     x = r(w*.5);
8484                     y = h;
8485                 break;
8486                 case "tl":
8487                     x = 0;
8488                     y = 0;
8489                 break;
8490                 case "bl":
8491                     x = 0;
8492                     y = h;
8493                 break;
8494                 case "br":
8495                     x = w;
8496                     y = h;
8497                 break;
8498                 case "tr":
8499                     x = w;
8500                     y = 0;
8501                 break;
8502             }
8503             if(local === true){
8504                 return [x, y];
8505             }
8506             if(vp){
8507                 var sc = this.getScroll();
8508                 return [x + sc.left, y + sc.top];
8509             }
8510             //Add the element's offset xy
8511             var o = this.getXY();
8512             return [x+o[0], y+o[1]];
8513         },
8514
8515         /**
8516          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8517          * supported position values.
8518          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8519          * @param {String} position The position to align to.
8520          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8521          * @return {Array} [x, y]
8522          */
8523         getAlignToXY : function(el, p, o){
8524             el = Roo.get(el);
8525             var d = this.dom;
8526             if(!el.dom){
8527                 throw "Element.alignTo with an element that doesn't exist";
8528             }
8529             var c = false; //constrain to viewport
8530             var p1 = "", p2 = "";
8531             o = o || [0,0];
8532
8533             if(!p){
8534                 p = "tl-bl";
8535             }else if(p == "?"){
8536                 p = "tl-bl?";
8537             }else if(p.indexOf("-") == -1){
8538                 p = "tl-" + p;
8539             }
8540             p = p.toLowerCase();
8541             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8542             if(!m){
8543                throw "Element.alignTo with an invalid alignment " + p;
8544             }
8545             p1 = m[1]; p2 = m[2]; c = !!m[3];
8546
8547             //Subtract the aligned el's internal xy from the target's offset xy
8548             //plus custom offset to get the aligned el's new offset xy
8549             var a1 = this.getAnchorXY(p1, true);
8550             var a2 = el.getAnchorXY(p2, false);
8551             var x = a2[0] - a1[0] + o[0];
8552             var y = a2[1] - a1[1] + o[1];
8553             if(c){
8554                 //constrain the aligned el to viewport if necessary
8555                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8556                 // 5px of margin for ie
8557                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8558
8559                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8560                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8561                 //otherwise swap the aligned el to the opposite border of the target.
8562                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8563                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8564                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8565                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8566
8567                var doc = document;
8568                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8569                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8570
8571                if((x+w) > dw + scrollX){
8572                     x = swapX ? r.left-w : dw+scrollX-w;
8573                 }
8574                if(x < scrollX){
8575                    x = swapX ? r.right : scrollX;
8576                }
8577                if((y+h) > dh + scrollY){
8578                     y = swapY ? r.top-h : dh+scrollY-h;
8579                 }
8580                if (y < scrollY){
8581                    y = swapY ? r.bottom : scrollY;
8582                }
8583             }
8584             return [x,y];
8585         },
8586
8587         // private
8588         getConstrainToXY : function(){
8589             var os = {top:0, left:0, bottom:0, right: 0};
8590
8591             return function(el, local, offsets, proposedXY){
8592                 el = Roo.get(el);
8593                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8594
8595                 var vw, vh, vx = 0, vy = 0;
8596                 if(el.dom == document.body || el.dom == document){
8597                     vw = Roo.lib.Dom.getViewWidth();
8598                     vh = Roo.lib.Dom.getViewHeight();
8599                 }else{
8600                     vw = el.dom.clientWidth;
8601                     vh = el.dom.clientHeight;
8602                     if(!local){
8603                         var vxy = el.getXY();
8604                         vx = vxy[0];
8605                         vy = vxy[1];
8606                     }
8607                 }
8608
8609                 var s = el.getScroll();
8610
8611                 vx += offsets.left + s.left;
8612                 vy += offsets.top + s.top;
8613
8614                 vw -= offsets.right;
8615                 vh -= offsets.bottom;
8616
8617                 var vr = vx+vw;
8618                 var vb = vy+vh;
8619
8620                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8621                 var x = xy[0], y = xy[1];
8622                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8623
8624                 // only move it if it needs it
8625                 var moved = false;
8626
8627                 // first validate right/bottom
8628                 if((x + w) > vr){
8629                     x = vr - w;
8630                     moved = true;
8631                 }
8632                 if((y + h) > vb){
8633                     y = vb - h;
8634                     moved = true;
8635                 }
8636                 // then make sure top/left isn't negative
8637                 if(x < vx){
8638                     x = vx;
8639                     moved = true;
8640                 }
8641                 if(y < vy){
8642                     y = vy;
8643                     moved = true;
8644                 }
8645                 return moved ? [x, y] : false;
8646             };
8647         }(),
8648
8649         // private
8650         adjustForConstraints : function(xy, parent, offsets){
8651             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8652         },
8653
8654         /**
8655          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8656          * document it aligns it to the viewport.
8657          * The position parameter is optional, and can be specified in any one of the following formats:
8658          * <ul>
8659          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8660          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8661          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8662          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8663          *   <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
8664          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8665          * </ul>
8666          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8667          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8668          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8669          * that specified in order to enforce the viewport constraints.
8670          * Following are all of the supported anchor positions:
8671     <pre>
8672     Value  Description
8673     -----  -----------------------------
8674     tl     The top left corner (default)
8675     t      The center of the top edge
8676     tr     The top right corner
8677     l      The center of the left edge
8678     c      In the center of the element
8679     r      The center of the right edge
8680     bl     The bottom left corner
8681     b      The center of the bottom edge
8682     br     The bottom right corner
8683     </pre>
8684     Example Usage:
8685     <pre><code>
8686     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8687     el.alignTo("other-el");
8688
8689     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8690     el.alignTo("other-el", "tr?");
8691
8692     // align the bottom right corner of el with the center left edge of other-el
8693     el.alignTo("other-el", "br-l?");
8694
8695     // align the center of el with the bottom left corner of other-el and
8696     // adjust the x position by -6 pixels (and the y position by 0)
8697     el.alignTo("other-el", "c-bl", [-6, 0]);
8698     </code></pre>
8699          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8700          * @param {String} position The position to align to.
8701          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8702          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8703          * @return {Roo.Element} this
8704          */
8705         alignTo : function(element, position, offsets, animate){
8706             var xy = this.getAlignToXY(element, position, offsets);
8707             this.setXY(xy, this.preanim(arguments, 3));
8708             return this;
8709         },
8710
8711         /**
8712          * Anchors an element to another element and realigns it when the window is resized.
8713          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8714          * @param {String} position The position to align to.
8715          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8716          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8717          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8718          * is a number, it is used as the buffer delay (defaults to 50ms).
8719          * @param {Function} callback The function to call after the animation finishes
8720          * @return {Roo.Element} this
8721          */
8722         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8723             var action = function(){
8724                 this.alignTo(el, alignment, offsets, animate);
8725                 Roo.callback(callback, this);
8726             };
8727             Roo.EventManager.onWindowResize(action, this);
8728             var tm = typeof monitorScroll;
8729             if(tm != 'undefined'){
8730                 Roo.EventManager.on(window, 'scroll', action, this,
8731                     {buffer: tm == 'number' ? monitorScroll : 50});
8732             }
8733             action.call(this); // align immediately
8734             return this;
8735         },
8736         /**
8737          * Clears any opacity settings from this element. Required in some cases for IE.
8738          * @return {Roo.Element} this
8739          */
8740         clearOpacity : function(){
8741             if (window.ActiveXObject) {
8742                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8743                     this.dom.style.filter = "";
8744                 }
8745             } else {
8746                 this.dom.style.opacity = "";
8747                 this.dom.style["-moz-opacity"] = "";
8748                 this.dom.style["-khtml-opacity"] = "";
8749             }
8750             return this;
8751         },
8752
8753         /**
8754          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8755          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8756          * @return {Roo.Element} this
8757          */
8758         hide : function(animate){
8759             this.setVisible(false, this.preanim(arguments, 0));
8760             return this;
8761         },
8762
8763         /**
8764         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8765         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8766          * @return {Roo.Element} this
8767          */
8768         show : function(animate){
8769             this.setVisible(true, this.preanim(arguments, 0));
8770             return this;
8771         },
8772
8773         /**
8774          * @private Test if size has a unit, otherwise appends the default
8775          */
8776         addUnits : function(size){
8777             return Roo.Element.addUnits(size, this.defaultUnit);
8778         },
8779
8780         /**
8781          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8782          * @return {Roo.Element} this
8783          */
8784         beginMeasure : function(){
8785             var el = this.dom;
8786             if(el.offsetWidth || el.offsetHeight){
8787                 return this; // offsets work already
8788             }
8789             var changed = [];
8790             var p = this.dom, b = document.body; // start with this element
8791             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8792                 var pe = Roo.get(p);
8793                 if(pe.getStyle('display') == 'none'){
8794                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8795                     p.style.visibility = "hidden";
8796                     p.style.display = "block";
8797                 }
8798                 p = p.parentNode;
8799             }
8800             this._measureChanged = changed;
8801             return this;
8802
8803         },
8804
8805         /**
8806          * Restores displays to before beginMeasure was called
8807          * @return {Roo.Element} this
8808          */
8809         endMeasure : function(){
8810             var changed = this._measureChanged;
8811             if(changed){
8812                 for(var i = 0, len = changed.length; i < len; i++) {
8813                     var r = changed[i];
8814                     r.el.style.visibility = r.visibility;
8815                     r.el.style.display = "none";
8816                 }
8817                 this._measureChanged = null;
8818             }
8819             return this;
8820         },
8821
8822         /**
8823         * Update the innerHTML of this element, optionally searching for and processing scripts
8824         * @param {String} html The new HTML
8825         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8826         * @param {Function} callback For async script loading you can be noticed when the update completes
8827         * @return {Roo.Element} this
8828          */
8829         update : function(html, loadScripts, callback){
8830             if(typeof html == "undefined"){
8831                 html = "";
8832             }
8833             if(loadScripts !== true){
8834                 this.dom.innerHTML = html;
8835                 if(typeof callback == "function"){
8836                     callback();
8837                 }
8838                 return this;
8839             }
8840             var id = Roo.id();
8841             var dom = this.dom;
8842
8843             html += '<span id="' + id + '"></span>';
8844
8845             E.onAvailable(id, function(){
8846                 var hd = document.getElementsByTagName("head")[0];
8847                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8848                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8849                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8850
8851                 var match;
8852                 while(match = re.exec(html)){
8853                     var attrs = match[1];
8854                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8855                     if(srcMatch && srcMatch[2]){
8856                        var s = document.createElement("script");
8857                        s.src = srcMatch[2];
8858                        var typeMatch = attrs.match(typeRe);
8859                        if(typeMatch && typeMatch[2]){
8860                            s.type = typeMatch[2];
8861                        }
8862                        hd.appendChild(s);
8863                     }else if(match[2] && match[2].length > 0){
8864                         if(window.execScript) {
8865                            window.execScript(match[2]);
8866                         } else {
8867                             /**
8868                              * eval:var:id
8869                              * eval:var:dom
8870                              * eval:var:html
8871                              * 
8872                              */
8873                            window.eval(match[2]);
8874                         }
8875                     }
8876                 }
8877                 var el = document.getElementById(id);
8878                 if(el){el.parentNode.removeChild(el);}
8879                 if(typeof callback == "function"){
8880                     callback();
8881                 }
8882             });
8883             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8884             return this;
8885         },
8886
8887         /**
8888          * Direct access to the UpdateManager update() method (takes the same parameters).
8889          * @param {String/Function} url The url for this request or a function to call to get the url
8890          * @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}
8891          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8892          * @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.
8893          * @return {Roo.Element} this
8894          */
8895         load : function(){
8896             var um = this.getUpdateManager();
8897             um.update.apply(um, arguments);
8898             return this;
8899         },
8900
8901         /**
8902         * Gets this element's UpdateManager
8903         * @return {Roo.UpdateManager} The UpdateManager
8904         */
8905         getUpdateManager : function(){
8906             if(!this.updateManager){
8907                 this.updateManager = new Roo.UpdateManager(this);
8908             }
8909             return this.updateManager;
8910         },
8911
8912         /**
8913          * Disables text selection for this element (normalized across browsers)
8914          * @return {Roo.Element} this
8915          */
8916         unselectable : function(){
8917             this.dom.unselectable = "on";
8918             this.swallowEvent("selectstart", true);
8919             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8920             this.addClass("x-unselectable");
8921             return this;
8922         },
8923
8924         /**
8925         * Calculates the x, y to center this element on the screen
8926         * @return {Array} The x, y values [x, y]
8927         */
8928         getCenterXY : function(){
8929             return this.getAlignToXY(document, 'c-c');
8930         },
8931
8932         /**
8933         * Centers the Element in either the viewport, or another Element.
8934         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8935         */
8936         center : function(centerIn){
8937             this.alignTo(centerIn || document, 'c-c');
8938             return this;
8939         },
8940
8941         /**
8942          * Tests various css rules/browsers to determine if this element uses a border box
8943          * @return {Boolean}
8944          */
8945         isBorderBox : function(){
8946             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8947         },
8948
8949         /**
8950          * Return a box {x, y, width, height} that can be used to set another elements
8951          * size/location to match this element.
8952          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8953          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8954          * @return {Object} box An object in the format {x, y, width, height}
8955          */
8956         getBox : function(contentBox, local){
8957             var xy;
8958             if(!local){
8959                 xy = this.getXY();
8960             }else{
8961                 var left = parseInt(this.getStyle("left"), 10) || 0;
8962                 var top = parseInt(this.getStyle("top"), 10) || 0;
8963                 xy = [left, top];
8964             }
8965             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8966             if(!contentBox){
8967                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8968             }else{
8969                 var l = this.getBorderWidth("l")+this.getPadding("l");
8970                 var r = this.getBorderWidth("r")+this.getPadding("r");
8971                 var t = this.getBorderWidth("t")+this.getPadding("t");
8972                 var b = this.getBorderWidth("b")+this.getPadding("b");
8973                 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)};
8974             }
8975             bx.right = bx.x + bx.width;
8976             bx.bottom = bx.y + bx.height;
8977             return bx;
8978         },
8979
8980         /**
8981          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8982          for more information about the sides.
8983          * @param {String} sides
8984          * @return {Number}
8985          */
8986         getFrameWidth : function(sides, onlyContentBox){
8987             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8988         },
8989
8990         /**
8991          * 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.
8992          * @param {Object} box The box to fill {x, y, width, height}
8993          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8994          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8995          * @return {Roo.Element} this
8996          */
8997         setBox : function(box, adjust, animate){
8998             var w = box.width, h = box.height;
8999             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9000                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9001                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9002             }
9003             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9004             return this;
9005         },
9006
9007         /**
9008          * Forces the browser to repaint this element
9009          * @return {Roo.Element} this
9010          */
9011          repaint : function(){
9012             var dom = this.dom;
9013             this.addClass("x-repaint");
9014             setTimeout(function(){
9015                 Roo.get(dom).removeClass("x-repaint");
9016             }, 1);
9017             return this;
9018         },
9019
9020         /**
9021          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9022          * then it returns the calculated width of the sides (see getPadding)
9023          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9024          * @return {Object/Number}
9025          */
9026         getMargins : function(side){
9027             if(!side){
9028                 return {
9029                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9030                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9031                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9032                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9033                 };
9034             }else{
9035                 return this.addStyles(side, El.margins);
9036              }
9037         },
9038
9039         // private
9040         addStyles : function(sides, styles){
9041             var val = 0, v, w;
9042             for(var i = 0, len = sides.length; i < len; i++){
9043                 v = this.getStyle(styles[sides.charAt(i)]);
9044                 if(v){
9045                      w = parseInt(v, 10);
9046                      if(w){ val += w; }
9047                 }
9048             }
9049             return val;
9050         },
9051
9052         /**
9053          * Creates a proxy element of this element
9054          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9055          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9056          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9057          * @return {Roo.Element} The new proxy element
9058          */
9059         createProxy : function(config, renderTo, matchBox){
9060             if(renderTo){
9061                 renderTo = Roo.getDom(renderTo);
9062             }else{
9063                 renderTo = document.body;
9064             }
9065             config = typeof config == "object" ?
9066                 config : {tag : "div", cls: config};
9067             var proxy = Roo.DomHelper.append(renderTo, config, true);
9068             if(matchBox){
9069                proxy.setBox(this.getBox());
9070             }
9071             return proxy;
9072         },
9073
9074         /**
9075          * Puts a mask over this element to disable user interaction. Requires core.css.
9076          * This method can only be applied to elements which accept child nodes.
9077          * @param {String} msg (optional) A message to display in the mask
9078          * @param {String} msgCls (optional) A css class to apply to the msg element
9079          * @return {Element} The mask  element
9080          */
9081         mask : function(msg, msgCls)
9082         {
9083             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9084                 this.setStyle("position", "relative");
9085             }
9086             if(!this._mask){
9087                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9088             }
9089             this.addClass("x-masked");
9090             this._mask.setDisplayed(true);
9091             
9092             // we wander
9093             var z = 0;
9094             var dom = this.dom;
9095             while (dom && dom.style) {
9096                 if (!isNaN(parseInt(dom.style.zIndex))) {
9097                     z = Math.max(z, parseInt(dom.style.zIndex));
9098                 }
9099                 dom = dom.parentNode;
9100             }
9101             // if we are masking the body - then it hides everything..
9102             if (this.dom == document.body) {
9103                 z = 1000000;
9104                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9105                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9106             }
9107            
9108             if(typeof msg == 'string'){
9109                 if(!this._maskMsg){
9110                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9111                 }
9112                 var mm = this._maskMsg;
9113                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9114                 if (mm.dom.firstChild) { // weird IE issue?
9115                     mm.dom.firstChild.innerHTML = msg;
9116                 }
9117                 mm.setDisplayed(true);
9118                 mm.center(this);
9119                 mm.setStyle('z-index', z + 102);
9120             }
9121             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9122                 this._mask.setHeight(this.getHeight());
9123             }
9124             this._mask.setStyle('z-index', z + 100);
9125             
9126             return this._mask;
9127         },
9128
9129         /**
9130          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9131          * it is cached for reuse.
9132          */
9133         unmask : function(removeEl){
9134             if(this._mask){
9135                 if(removeEl === true){
9136                     this._mask.remove();
9137                     delete this._mask;
9138                     if(this._maskMsg){
9139                         this._maskMsg.remove();
9140                         delete this._maskMsg;
9141                     }
9142                 }else{
9143                     this._mask.setDisplayed(false);
9144                     if(this._maskMsg){
9145                         this._maskMsg.setDisplayed(false);
9146                     }
9147                 }
9148             }
9149             this.removeClass("x-masked");
9150         },
9151
9152         /**
9153          * Returns true if this element is masked
9154          * @return {Boolean}
9155          */
9156         isMasked : function(){
9157             return this._mask && this._mask.isVisible();
9158         },
9159
9160         /**
9161          * Creates an iframe shim for this element to keep selects and other windowed objects from
9162          * showing through.
9163          * @return {Roo.Element} The new shim element
9164          */
9165         createShim : function(){
9166             var el = document.createElement('iframe');
9167             el.frameBorder = 'no';
9168             el.className = 'roo-shim';
9169             if(Roo.isIE && Roo.isSecure){
9170                 el.src = Roo.SSL_SECURE_URL;
9171             }
9172             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9173             shim.autoBoxAdjust = false;
9174             return shim;
9175         },
9176
9177         /**
9178          * Removes this element from the DOM and deletes it from the cache
9179          */
9180         remove : function(){
9181             if(this.dom.parentNode){
9182                 this.dom.parentNode.removeChild(this.dom);
9183             }
9184             delete El.cache[this.dom.id];
9185         },
9186
9187         /**
9188          * Sets up event handlers to add and remove a css class when the mouse is over this element
9189          * @param {String} className
9190          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9191          * mouseout events for children elements
9192          * @return {Roo.Element} this
9193          */
9194         addClassOnOver : function(className, preventFlicker){
9195             this.on("mouseover", function(){
9196                 Roo.fly(this, '_internal').addClass(className);
9197             }, this.dom);
9198             var removeFn = function(e){
9199                 if(preventFlicker !== true || !e.within(this, true)){
9200                     Roo.fly(this, '_internal').removeClass(className);
9201                 }
9202             };
9203             this.on("mouseout", removeFn, this.dom);
9204             return this;
9205         },
9206
9207         /**
9208          * Sets up event handlers to add and remove a css class when this element has the focus
9209          * @param {String} className
9210          * @return {Roo.Element} this
9211          */
9212         addClassOnFocus : function(className){
9213             this.on("focus", function(){
9214                 Roo.fly(this, '_internal').addClass(className);
9215             }, this.dom);
9216             this.on("blur", function(){
9217                 Roo.fly(this, '_internal').removeClass(className);
9218             }, this.dom);
9219             return this;
9220         },
9221         /**
9222          * 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)
9223          * @param {String} className
9224          * @return {Roo.Element} this
9225          */
9226         addClassOnClick : function(className){
9227             var dom = this.dom;
9228             this.on("mousedown", function(){
9229                 Roo.fly(dom, '_internal').addClass(className);
9230                 var d = Roo.get(document);
9231                 var fn = function(){
9232                     Roo.fly(dom, '_internal').removeClass(className);
9233                     d.removeListener("mouseup", fn);
9234                 };
9235                 d.on("mouseup", fn);
9236             });
9237             return this;
9238         },
9239
9240         /**
9241          * Stops the specified event from bubbling and optionally prevents the default action
9242          * @param {String} eventName
9243          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9244          * @return {Roo.Element} this
9245          */
9246         swallowEvent : function(eventName, preventDefault){
9247             var fn = function(e){
9248                 e.stopPropagation();
9249                 if(preventDefault){
9250                     e.preventDefault();
9251                 }
9252             };
9253             if(eventName instanceof Array){
9254                 for(var i = 0, len = eventName.length; i < len; i++){
9255                      this.on(eventName[i], fn);
9256                 }
9257                 return this;
9258             }
9259             this.on(eventName, fn);
9260             return this;
9261         },
9262
9263         /**
9264          * @private
9265          */
9266       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9267
9268         /**
9269          * Sizes this element to its parent element's dimensions performing
9270          * neccessary box adjustments.
9271          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9272          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9273          * @return {Roo.Element} this
9274          */
9275         fitToParent : function(monitorResize, targetParent) {
9276           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9277           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9278           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9279             return;
9280           }
9281           var p = Roo.get(targetParent || this.dom.parentNode);
9282           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9283           if (monitorResize === true) {
9284             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9285             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9286           }
9287           return this;
9288         },
9289
9290         /**
9291          * Gets the next sibling, skipping text nodes
9292          * @return {HTMLElement} The next sibling or null
9293          */
9294         getNextSibling : function(){
9295             var n = this.dom.nextSibling;
9296             while(n && n.nodeType != 1){
9297                 n = n.nextSibling;
9298             }
9299             return n;
9300         },
9301
9302         /**
9303          * Gets the previous sibling, skipping text nodes
9304          * @return {HTMLElement} The previous sibling or null
9305          */
9306         getPrevSibling : function(){
9307             var n = this.dom.previousSibling;
9308             while(n && n.nodeType != 1){
9309                 n = n.previousSibling;
9310             }
9311             return n;
9312         },
9313
9314
9315         /**
9316          * Appends the passed element(s) to this element
9317          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9318          * @return {Roo.Element} this
9319          */
9320         appendChild: function(el){
9321             el = Roo.get(el);
9322             el.appendTo(this);
9323             return this;
9324         },
9325
9326         /**
9327          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9328          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9329          * automatically generated with the specified attributes.
9330          * @param {HTMLElement} insertBefore (optional) a child element of this element
9331          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9332          * @return {Roo.Element} The new child element
9333          */
9334         createChild: function(config, insertBefore, returnDom){
9335             config = config || {tag:'div'};
9336             if(insertBefore){
9337                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9338             }
9339             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9340         },
9341
9342         /**
9343          * Appends this element to the passed element
9344          * @param {String/HTMLElement/Element} el The new parent element
9345          * @return {Roo.Element} this
9346          */
9347         appendTo: function(el){
9348             el = Roo.getDom(el);
9349             el.appendChild(this.dom);
9350             return this;
9351         },
9352
9353         /**
9354          * Inserts this element before the passed element in the DOM
9355          * @param {String/HTMLElement/Element} el The element to insert before
9356          * @return {Roo.Element} this
9357          */
9358         insertBefore: function(el){
9359             el = Roo.getDom(el);
9360             el.parentNode.insertBefore(this.dom, el);
9361             return this;
9362         },
9363
9364         /**
9365          * Inserts this element after the passed element in the DOM
9366          * @param {String/HTMLElement/Element} el The element to insert after
9367          * @return {Roo.Element} this
9368          */
9369         insertAfter: function(el){
9370             el = Roo.getDom(el);
9371             el.parentNode.insertBefore(this.dom, el.nextSibling);
9372             return this;
9373         },
9374
9375         /**
9376          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9377          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9378          * @return {Roo.Element} The new child
9379          */
9380         insertFirst: function(el, returnDom){
9381             el = el || {};
9382             if(typeof el == 'object' && !el.nodeType){ // dh config
9383                 return this.createChild(el, this.dom.firstChild, returnDom);
9384             }else{
9385                 el = Roo.getDom(el);
9386                 this.dom.insertBefore(el, this.dom.firstChild);
9387                 return !returnDom ? Roo.get(el) : el;
9388             }
9389         },
9390
9391         /**
9392          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9393          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9394          * @param {String} where (optional) 'before' or 'after' defaults to before
9395          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9396          * @return {Roo.Element} the inserted Element
9397          */
9398         insertSibling: function(el, where, returnDom){
9399             where = where ? where.toLowerCase() : 'before';
9400             el = el || {};
9401             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9402
9403             if(typeof el == 'object' && !el.nodeType){ // dh config
9404                 if(where == 'after' && !this.dom.nextSibling){
9405                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9406                 }else{
9407                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9408                 }
9409
9410             }else{
9411                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9412                             where == 'before' ? this.dom : this.dom.nextSibling);
9413                 if(!returnDom){
9414                     rt = Roo.get(rt);
9415                 }
9416             }
9417             return rt;
9418         },
9419
9420         /**
9421          * Creates and wraps this element with another element
9422          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9423          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9424          * @return {HTMLElement/Element} The newly created wrapper element
9425          */
9426         wrap: function(config, returnDom){
9427             if(!config){
9428                 config = {tag: "div"};
9429             }
9430             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9431             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9432             return newEl;
9433         },
9434
9435         /**
9436          * Replaces the passed element with this element
9437          * @param {String/HTMLElement/Element} el The element to replace
9438          * @return {Roo.Element} this
9439          */
9440         replace: function(el){
9441             el = Roo.get(el);
9442             this.insertBefore(el);
9443             el.remove();
9444             return this;
9445         },
9446
9447         /**
9448          * Inserts an html fragment into this element
9449          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9450          * @param {String} html The HTML fragment
9451          * @param {Boolean} returnEl True to return an Roo.Element
9452          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9453          */
9454         insertHtml : function(where, html, returnEl){
9455             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9456             return returnEl ? Roo.get(el) : el;
9457         },
9458
9459         /**
9460          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9461          * @param {Object} o The object with the attributes
9462          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9463          * @return {Roo.Element} this
9464          */
9465         set : function(o, useSet){
9466             var el = this.dom;
9467             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9468             for(var attr in o){
9469                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9470                 if(attr=="cls"){
9471                     el.className = o["cls"];
9472                 }else{
9473                     if(useSet) {
9474                         el.setAttribute(attr, o[attr]);
9475                     } else {
9476                         el[attr] = o[attr];
9477                     }
9478                 }
9479             }
9480             if(o.style){
9481                 Roo.DomHelper.applyStyles(el, o.style);
9482             }
9483             return this;
9484         },
9485
9486         /**
9487          * Convenience method for constructing a KeyMap
9488          * @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:
9489          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9490          * @param {Function} fn The function to call
9491          * @param {Object} scope (optional) The scope of the function
9492          * @return {Roo.KeyMap} The KeyMap created
9493          */
9494         addKeyListener : function(key, fn, scope){
9495             var config;
9496             if(typeof key != "object" || key instanceof Array){
9497                 config = {
9498                     key: key,
9499                     fn: fn,
9500                     scope: scope
9501                 };
9502             }else{
9503                 config = {
9504                     key : key.key,
9505                     shift : key.shift,
9506                     ctrl : key.ctrl,
9507                     alt : key.alt,
9508                     fn: fn,
9509                     scope: scope
9510                 };
9511             }
9512             return new Roo.KeyMap(this, config);
9513         },
9514
9515         /**
9516          * Creates a KeyMap for this element
9517          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9518          * @return {Roo.KeyMap} The KeyMap created
9519          */
9520         addKeyMap : function(config){
9521             return new Roo.KeyMap(this, config);
9522         },
9523
9524         /**
9525          * Returns true if this element is scrollable.
9526          * @return {Boolean}
9527          */
9528          isScrollable : function(){
9529             var dom = this.dom;
9530             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9531         },
9532
9533         /**
9534          * 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().
9535          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9536          * @param {Number} value The new scroll value
9537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9538          * @return {Element} this
9539          */
9540
9541         scrollTo : function(side, value, animate){
9542             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9543             if(!animate || !A){
9544                 this.dom[prop] = value;
9545             }else{
9546                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9547                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9548             }
9549             return this;
9550         },
9551
9552         /**
9553          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9554          * within this element's scrollable range.
9555          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9556          * @param {Number} distance How far to scroll the element in pixels
9557          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9558          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9559          * was scrolled as far as it could go.
9560          */
9561          scroll : function(direction, distance, animate){
9562              if(!this.isScrollable()){
9563                  return;
9564              }
9565              var el = this.dom;
9566              var l = el.scrollLeft, t = el.scrollTop;
9567              var w = el.scrollWidth, h = el.scrollHeight;
9568              var cw = el.clientWidth, ch = el.clientHeight;
9569              direction = direction.toLowerCase();
9570              var scrolled = false;
9571              var a = this.preanim(arguments, 2);
9572              switch(direction){
9573                  case "l":
9574                  case "left":
9575                      if(w - l > cw){
9576                          var v = Math.min(l + distance, w-cw);
9577                          this.scrollTo("left", v, a);
9578                          scrolled = true;
9579                      }
9580                      break;
9581                 case "r":
9582                 case "right":
9583                      if(l > 0){
9584                          var v = Math.max(l - distance, 0);
9585                          this.scrollTo("left", v, a);
9586                          scrolled = true;
9587                      }
9588                      break;
9589                 case "t":
9590                 case "top":
9591                 case "up":
9592                      if(t > 0){
9593                          var v = Math.max(t - distance, 0);
9594                          this.scrollTo("top", v, a);
9595                          scrolled = true;
9596                      }
9597                      break;
9598                 case "b":
9599                 case "bottom":
9600                 case "down":
9601                      if(h - t > ch){
9602                          var v = Math.min(t + distance, h-ch);
9603                          this.scrollTo("top", v, a);
9604                          scrolled = true;
9605                      }
9606                      break;
9607              }
9608              return scrolled;
9609         },
9610
9611         /**
9612          * Translates the passed page coordinates into left/top css values for this element
9613          * @param {Number/Array} x The page x or an array containing [x, y]
9614          * @param {Number} y The page y
9615          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9616          */
9617         translatePoints : function(x, y){
9618             if(typeof x == 'object' || x instanceof Array){
9619                 y = x[1]; x = x[0];
9620             }
9621             var p = this.getStyle('position');
9622             var o = this.getXY();
9623
9624             var l = parseInt(this.getStyle('left'), 10);
9625             var t = parseInt(this.getStyle('top'), 10);
9626
9627             if(isNaN(l)){
9628                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9629             }
9630             if(isNaN(t)){
9631                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9632             }
9633
9634             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9635         },
9636
9637         /**
9638          * Returns the current scroll position of the element.
9639          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9640          */
9641         getScroll : function(){
9642             var d = this.dom, doc = document;
9643             if(d == doc || d == doc.body){
9644                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9645                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9646                 return {left: l, top: t};
9647             }else{
9648                 return {left: d.scrollLeft, top: d.scrollTop};
9649             }
9650         },
9651
9652         /**
9653          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9654          * are convert to standard 6 digit hex color.
9655          * @param {String} attr The css attribute
9656          * @param {String} defaultValue The default value to use when a valid color isn't found
9657          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9658          * YUI color anims.
9659          */
9660         getColor : function(attr, defaultValue, prefix){
9661             var v = this.getStyle(attr);
9662             if(!v || v == "transparent" || v == "inherit") {
9663                 return defaultValue;
9664             }
9665             var color = typeof prefix == "undefined" ? "#" : prefix;
9666             if(v.substr(0, 4) == "rgb("){
9667                 var rvs = v.slice(4, v.length -1).split(",");
9668                 for(var i = 0; i < 3; i++){
9669                     var h = parseInt(rvs[i]).toString(16);
9670                     if(h < 16){
9671                         h = "0" + h;
9672                     }
9673                     color += h;
9674                 }
9675             } else {
9676                 if(v.substr(0, 1) == "#"){
9677                     if(v.length == 4) {
9678                         for(var i = 1; i < 4; i++){
9679                             var c = v.charAt(i);
9680                             color +=  c + c;
9681                         }
9682                     }else if(v.length == 7){
9683                         color += v.substr(1);
9684                     }
9685                 }
9686             }
9687             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9688         },
9689
9690         /**
9691          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9692          * gradient background, rounded corners and a 4-way shadow.
9693          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9694          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9695          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9696          * @return {Roo.Element} this
9697          */
9698         boxWrap : function(cls){
9699             cls = cls || 'x-box';
9700             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9701             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9702             return el;
9703         },
9704
9705         /**
9706          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9707          * @param {String} namespace The namespace in which to look for the attribute
9708          * @param {String} name The attribute name
9709          * @return {String} The attribute value
9710          */
9711         getAttributeNS : Roo.isIE ? function(ns, name){
9712             var d = this.dom;
9713             var type = typeof d[ns+":"+name];
9714             if(type != 'undefined' && type != 'unknown'){
9715                 return d[ns+":"+name];
9716             }
9717             return d[name];
9718         } : function(ns, name){
9719             var d = this.dom;
9720             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9721         },
9722         
9723         
9724         /**
9725          * Sets or Returns the value the dom attribute value
9726          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9727          * @param {String} value (optional) The value to set the attribute to
9728          * @return {String} The attribute value
9729          */
9730         attr : function(name){
9731             if (arguments.length > 1) {
9732                 this.dom.setAttribute(name, arguments[1]);
9733                 return arguments[1];
9734             }
9735             if (typeof(name) == 'object') {
9736                 for(var i in name) {
9737                     this.attr(i, name[i]);
9738                 }
9739                 return name;
9740             }
9741             
9742             
9743             if (!this.dom.hasAttribute(name)) {
9744                 return undefined;
9745             }
9746             return this.dom.getAttribute(name);
9747         }
9748         
9749         
9750         
9751     };
9752
9753     var ep = El.prototype;
9754
9755     /**
9756      * Appends an event handler (Shorthand for addListener)
9757      * @param {String}   eventName     The type of event to append
9758      * @param {Function} fn        The method the event invokes
9759      * @param {Object} scope       (optional) The scope (this object) of the fn
9760      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9761      * @method
9762      */
9763     ep.on = ep.addListener;
9764         // backwards compat
9765     ep.mon = ep.addListener;
9766
9767     /**
9768      * Removes an event handler from this element (shorthand for removeListener)
9769      * @param {String} eventName the type of event to remove
9770      * @param {Function} fn the method the event invokes
9771      * @return {Roo.Element} this
9772      * @method
9773      */
9774     ep.un = ep.removeListener;
9775
9776     /**
9777      * true to automatically adjust width and height settings for box-model issues (default to true)
9778      */
9779     ep.autoBoxAdjust = true;
9780
9781     // private
9782     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9783
9784     // private
9785     El.addUnits = function(v, defaultUnit){
9786         if(v === "" || v == "auto"){
9787             return v;
9788         }
9789         if(v === undefined){
9790             return '';
9791         }
9792         if(typeof v == "number" || !El.unitPattern.test(v)){
9793             return v + (defaultUnit || 'px');
9794         }
9795         return v;
9796     };
9797
9798     // special markup used throughout Roo when box wrapping elements
9799     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>';
9800     /**
9801      * Visibility mode constant - Use visibility to hide element
9802      * @static
9803      * @type Number
9804      */
9805     El.VISIBILITY = 1;
9806     /**
9807      * Visibility mode constant - Use display to hide element
9808      * @static
9809      * @type Number
9810      */
9811     El.DISPLAY = 2;
9812
9813     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9814     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9815     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9816
9817
9818
9819     /**
9820      * @private
9821      */
9822     El.cache = {};
9823
9824     var docEl;
9825
9826     /**
9827      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9828      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9829      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9830      * @return {Element} The Element object
9831      * @static
9832      */
9833     El.get = function(el){
9834         var ex, elm, id;
9835         if(!el){ return null; }
9836         if(typeof el == "string"){ // element id
9837             if(!(elm = document.getElementById(el))){
9838                 return null;
9839             }
9840             if(ex = El.cache[el]){
9841                 ex.dom = elm;
9842             }else{
9843                 ex = El.cache[el] = new El(elm);
9844             }
9845             return ex;
9846         }else if(el.tagName){ // dom element
9847             if(!(id = el.id)){
9848                 id = Roo.id(el);
9849             }
9850             if(ex = El.cache[id]){
9851                 ex.dom = el;
9852             }else{
9853                 ex = El.cache[id] = new El(el);
9854             }
9855             return ex;
9856         }else if(el instanceof El){
9857             if(el != docEl){
9858                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9859                                                               // catch case where it hasn't been appended
9860                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9861             }
9862             return el;
9863         }else if(el.isComposite){
9864             return el;
9865         }else if(el instanceof Array){
9866             return El.select(el);
9867         }else if(el == document){
9868             // create a bogus element object representing the document object
9869             if(!docEl){
9870                 var f = function(){};
9871                 f.prototype = El.prototype;
9872                 docEl = new f();
9873                 docEl.dom = document;
9874             }
9875             return docEl;
9876         }
9877         return null;
9878     };
9879
9880     // private
9881     El.uncache = function(el){
9882         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9883             if(a[i]){
9884                 delete El.cache[a[i].id || a[i]];
9885             }
9886         }
9887     };
9888
9889     // private
9890     // Garbage collection - uncache elements/purge listeners on orphaned elements
9891     // so we don't hold a reference and cause the browser to retain them
9892     El.garbageCollect = function(){
9893         if(!Roo.enableGarbageCollector){
9894             clearInterval(El.collectorThread);
9895             return;
9896         }
9897         for(var eid in El.cache){
9898             var el = El.cache[eid], d = el.dom;
9899             // -------------------------------------------------------
9900             // Determining what is garbage:
9901             // -------------------------------------------------------
9902             // !d
9903             // dom node is null, definitely garbage
9904             // -------------------------------------------------------
9905             // !d.parentNode
9906             // no parentNode == direct orphan, definitely garbage
9907             // -------------------------------------------------------
9908             // !d.offsetParent && !document.getElementById(eid)
9909             // display none elements have no offsetParent so we will
9910             // also try to look it up by it's id. However, check
9911             // offsetParent first so we don't do unneeded lookups.
9912             // This enables collection of elements that are not orphans
9913             // directly, but somewhere up the line they have an orphan
9914             // parent.
9915             // -------------------------------------------------------
9916             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9917                 delete El.cache[eid];
9918                 if(d && Roo.enableListenerCollection){
9919                     E.purgeElement(d);
9920                 }
9921             }
9922         }
9923     }
9924     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9925
9926
9927     // dom is optional
9928     El.Flyweight = function(dom){
9929         this.dom = dom;
9930     };
9931     El.Flyweight.prototype = El.prototype;
9932
9933     El._flyweights = {};
9934     /**
9935      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9936      * the dom node can be overwritten by other code.
9937      * @param {String/HTMLElement} el The dom node or id
9938      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9939      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9940      * @static
9941      * @return {Element} The shared Element object
9942      */
9943     El.fly = function(el, named){
9944         named = named || '_global';
9945         el = Roo.getDom(el);
9946         if(!el){
9947             return null;
9948         }
9949         if(!El._flyweights[named]){
9950             El._flyweights[named] = new El.Flyweight();
9951         }
9952         El._flyweights[named].dom = el;
9953         return El._flyweights[named];
9954     };
9955
9956     /**
9957      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9958      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9959      * Shorthand of {@link Roo.Element#get}
9960      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9961      * @return {Element} The Element object
9962      * @member Roo
9963      * @method get
9964      */
9965     Roo.get = El.get;
9966     /**
9967      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9968      * the dom node can be overwritten by other code.
9969      * Shorthand of {@link Roo.Element#fly}
9970      * @param {String/HTMLElement} el The dom node or id
9971      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9972      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9973      * @static
9974      * @return {Element} The shared Element object
9975      * @member Roo
9976      * @method fly
9977      */
9978     Roo.fly = El.fly;
9979
9980     // speedy lookup for elements never to box adjust
9981     var noBoxAdjust = Roo.isStrict ? {
9982         select:1
9983     } : {
9984         input:1, select:1, textarea:1
9985     };
9986     if(Roo.isIE || Roo.isGecko){
9987         noBoxAdjust['button'] = 1;
9988     }
9989
9990
9991     Roo.EventManager.on(window, 'unload', function(){
9992         delete El.cache;
9993         delete El._flyweights;
9994     });
9995 })();
9996
9997
9998
9999
10000 if(Roo.DomQuery){
10001     Roo.Element.selectorFunction = Roo.DomQuery.select;
10002 }
10003
10004 Roo.Element.select = function(selector, unique, root){
10005     var els;
10006     if(typeof selector == "string"){
10007         els = Roo.Element.selectorFunction(selector, root);
10008     }else if(selector.length !== undefined){
10009         els = selector;
10010     }else{
10011         throw "Invalid selector";
10012     }
10013     if(unique === true){
10014         return new Roo.CompositeElement(els);
10015     }else{
10016         return new Roo.CompositeElementLite(els);
10017     }
10018 };
10019 /**
10020  * Selects elements based on the passed CSS selector to enable working on them as 1.
10021  * @param {String/Array} selector The CSS selector or an array of elements
10022  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10023  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10024  * @return {CompositeElementLite/CompositeElement}
10025  * @member Roo
10026  * @method select
10027  */
10028 Roo.select = Roo.Element.select;
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043 /*
10044  * Based on:
10045  * Ext JS Library 1.1.1
10046  * Copyright(c) 2006-2007, Ext JS, LLC.
10047  *
10048  * Originally Released Under LGPL - original licence link has changed is not relivant.
10049  *
10050  * Fork - LGPL
10051  * <script type="text/javascript">
10052  */
10053
10054
10055
10056 //Notifies Element that fx methods are available
10057 Roo.enableFx = true;
10058
10059 /**
10060  * @class Roo.Fx
10061  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10062  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10063  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10064  * Element effects to work.</p><br/>
10065  *
10066  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10067  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10068  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10069  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10070  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10071  * expected results and should be done with care.</p><br/>
10072  *
10073  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10074  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10075 <pre>
10076 Value  Description
10077 -----  -----------------------------
10078 tl     The top left corner
10079 t      The center of the top edge
10080 tr     The top right corner
10081 l      The center of the left edge
10082 r      The center of the right edge
10083 bl     The bottom left corner
10084 b      The center of the bottom edge
10085 br     The bottom right corner
10086 </pre>
10087  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10088  * below are common options that can be passed to any Fx method.</b>
10089  * @cfg {Function} callback A function called when the effect is finished
10090  * @cfg {Object} scope The scope of the effect function
10091  * @cfg {String} easing A valid Easing value for the effect
10092  * @cfg {String} afterCls A css class to apply after the effect
10093  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10094  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10095  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10096  * effects that end with the element being visually hidden, ignored otherwise)
10097  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10098  * a function which returns such a specification that will be applied to the Element after the effect finishes
10099  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10100  * @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
10101  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10102  */
10103 Roo.Fx = {
10104         /**
10105          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10106          * origin for the slide effect.  This function automatically handles wrapping the element with
10107          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10108          * Usage:
10109          *<pre><code>
10110 // default: slide the element in from the top
10111 el.slideIn();
10112
10113 // custom: slide the element in from the right with a 2-second duration
10114 el.slideIn('r', { duration: 2 });
10115
10116 // common config options shown with default values
10117 el.slideIn('t', {
10118     easing: 'easeOut',
10119     duration: .5
10120 });
10121 </code></pre>
10122          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10123          * @param {Object} options (optional) Object literal with any of the Fx config options
10124          * @return {Roo.Element} The Element
10125          */
10126     slideIn : function(anchor, o){
10127         var el = this.getFxEl();
10128         o = o || {};
10129
10130         el.queueFx(o, function(){
10131
10132             anchor = anchor || "t";
10133
10134             // fix display to visibility
10135             this.fixDisplay();
10136
10137             // restore values after effect
10138             var r = this.getFxRestore();
10139             var b = this.getBox();
10140             // fixed size for slide
10141             this.setSize(b);
10142
10143             // wrap if needed
10144             var wrap = this.fxWrap(r.pos, o, "hidden");
10145
10146             var st = this.dom.style;
10147             st.visibility = "visible";
10148             st.position = "absolute";
10149
10150             // clear out temp styles after slide and unwrap
10151             var after = function(){
10152                 el.fxUnwrap(wrap, r.pos, o);
10153                 st.width = r.width;
10154                 st.height = r.height;
10155                 el.afterFx(o);
10156             };
10157             // time to calc the positions
10158             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10159
10160             switch(anchor.toLowerCase()){
10161                 case "t":
10162                     wrap.setSize(b.width, 0);
10163                     st.left = st.bottom = "0";
10164                     a = {height: bh};
10165                 break;
10166                 case "l":
10167                     wrap.setSize(0, b.height);
10168                     st.right = st.top = "0";
10169                     a = {width: bw};
10170                 break;
10171                 case "r":
10172                     wrap.setSize(0, b.height);
10173                     wrap.setX(b.right);
10174                     st.left = st.top = "0";
10175                     a = {width: bw, points: pt};
10176                 break;
10177                 case "b":
10178                     wrap.setSize(b.width, 0);
10179                     wrap.setY(b.bottom);
10180                     st.left = st.top = "0";
10181                     a = {height: bh, points: pt};
10182                 break;
10183                 case "tl":
10184                     wrap.setSize(0, 0);
10185                     st.right = st.bottom = "0";
10186                     a = {width: bw, height: bh};
10187                 break;
10188                 case "bl":
10189                     wrap.setSize(0, 0);
10190                     wrap.setY(b.y+b.height);
10191                     st.right = st.top = "0";
10192                     a = {width: bw, height: bh, points: pt};
10193                 break;
10194                 case "br":
10195                     wrap.setSize(0, 0);
10196                     wrap.setXY([b.right, b.bottom]);
10197                     st.left = st.top = "0";
10198                     a = {width: bw, height: bh, points: pt};
10199                 break;
10200                 case "tr":
10201                     wrap.setSize(0, 0);
10202                     wrap.setX(b.x+b.width);
10203                     st.left = st.bottom = "0";
10204                     a = {width: bw, height: bh, points: pt};
10205                 break;
10206             }
10207             this.dom.style.visibility = "visible";
10208             wrap.show();
10209
10210             arguments.callee.anim = wrap.fxanim(a,
10211                 o,
10212                 'motion',
10213                 .5,
10214                 'easeOut', after);
10215         });
10216         return this;
10217     },
10218     
10219         /**
10220          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10221          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10222          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10223          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10224          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10225          * Usage:
10226          *<pre><code>
10227 // default: slide the element out to the top
10228 el.slideOut();
10229
10230 // custom: slide the element out to the right with a 2-second duration
10231 el.slideOut('r', { duration: 2 });
10232
10233 // common config options shown with default values
10234 el.slideOut('t', {
10235     easing: 'easeOut',
10236     duration: .5,
10237     remove: false,
10238     useDisplay: false
10239 });
10240 </code></pre>
10241          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10242          * @param {Object} options (optional) Object literal with any of the Fx config options
10243          * @return {Roo.Element} The Element
10244          */
10245     slideOut : function(anchor, o){
10246         var el = this.getFxEl();
10247         o = o || {};
10248
10249         el.queueFx(o, function(){
10250
10251             anchor = anchor || "t";
10252
10253             // restore values after effect
10254             var r = this.getFxRestore();
10255             
10256             var b = this.getBox();
10257             // fixed size for slide
10258             this.setSize(b);
10259
10260             // wrap if needed
10261             var wrap = this.fxWrap(r.pos, o, "visible");
10262
10263             var st = this.dom.style;
10264             st.visibility = "visible";
10265             st.position = "absolute";
10266
10267             wrap.setSize(b);
10268
10269             var after = function(){
10270                 if(o.useDisplay){
10271                     el.setDisplayed(false);
10272                 }else{
10273                     el.hide();
10274                 }
10275
10276                 el.fxUnwrap(wrap, r.pos, o);
10277
10278                 st.width = r.width;
10279                 st.height = r.height;
10280
10281                 el.afterFx(o);
10282             };
10283
10284             var a, zero = {to: 0};
10285             switch(anchor.toLowerCase()){
10286                 case "t":
10287                     st.left = st.bottom = "0";
10288                     a = {height: zero};
10289                 break;
10290                 case "l":
10291                     st.right = st.top = "0";
10292                     a = {width: zero};
10293                 break;
10294                 case "r":
10295                     st.left = st.top = "0";
10296                     a = {width: zero, points: {to:[b.right, b.y]}};
10297                 break;
10298                 case "b":
10299                     st.left = st.top = "0";
10300                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10301                 break;
10302                 case "tl":
10303                     st.right = st.bottom = "0";
10304                     a = {width: zero, height: zero};
10305                 break;
10306                 case "bl":
10307                     st.right = st.top = "0";
10308                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10309                 break;
10310                 case "br":
10311                     st.left = st.top = "0";
10312                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10313                 break;
10314                 case "tr":
10315                     st.left = st.bottom = "0";
10316                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10317                 break;
10318             }
10319
10320             arguments.callee.anim = wrap.fxanim(a,
10321                 o,
10322                 'motion',
10323                 .5,
10324                 "easeOut", after);
10325         });
10326         return this;
10327     },
10328
10329         /**
10330          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10331          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10332          * The element must be removed from the DOM using the 'remove' config option if desired.
10333          * Usage:
10334          *<pre><code>
10335 // default
10336 el.puff();
10337
10338 // common config options shown with default values
10339 el.puff({
10340     easing: 'easeOut',
10341     duration: .5,
10342     remove: false,
10343     useDisplay: false
10344 });
10345 </code></pre>
10346          * @param {Object} options (optional) Object literal with any of the Fx config options
10347          * @return {Roo.Element} The Element
10348          */
10349     puff : function(o){
10350         var el = this.getFxEl();
10351         o = o || {};
10352
10353         el.queueFx(o, function(){
10354             this.clearOpacity();
10355             this.show();
10356
10357             // restore values after effect
10358             var r = this.getFxRestore();
10359             var st = this.dom.style;
10360
10361             var after = function(){
10362                 if(o.useDisplay){
10363                     el.setDisplayed(false);
10364                 }else{
10365                     el.hide();
10366                 }
10367
10368                 el.clearOpacity();
10369
10370                 el.setPositioning(r.pos);
10371                 st.width = r.width;
10372                 st.height = r.height;
10373                 st.fontSize = '';
10374                 el.afterFx(o);
10375             };
10376
10377             var width = this.getWidth();
10378             var height = this.getHeight();
10379
10380             arguments.callee.anim = this.fxanim({
10381                     width : {to: this.adjustWidth(width * 2)},
10382                     height : {to: this.adjustHeight(height * 2)},
10383                     points : {by: [-(width * .5), -(height * .5)]},
10384                     opacity : {to: 0},
10385                     fontSize: {to:200, unit: "%"}
10386                 },
10387                 o,
10388                 'motion',
10389                 .5,
10390                 "easeOut", after);
10391         });
10392         return this;
10393     },
10394
10395         /**
10396          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10397          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10398          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10399          * Usage:
10400          *<pre><code>
10401 // default
10402 el.switchOff();
10403
10404 // all config options shown with default values
10405 el.switchOff({
10406     easing: 'easeIn',
10407     duration: .3,
10408     remove: false,
10409     useDisplay: false
10410 });
10411 </code></pre>
10412          * @param {Object} options (optional) Object literal with any of the Fx config options
10413          * @return {Roo.Element} The Element
10414          */
10415     switchOff : function(o){
10416         var el = this.getFxEl();
10417         o = o || {};
10418
10419         el.queueFx(o, function(){
10420             this.clearOpacity();
10421             this.clip();
10422
10423             // restore values after effect
10424             var r = this.getFxRestore();
10425             var st = this.dom.style;
10426
10427             var after = function(){
10428                 if(o.useDisplay){
10429                     el.setDisplayed(false);
10430                 }else{
10431                     el.hide();
10432                 }
10433
10434                 el.clearOpacity();
10435                 el.setPositioning(r.pos);
10436                 st.width = r.width;
10437                 st.height = r.height;
10438
10439                 el.afterFx(o);
10440             };
10441
10442             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10443                 this.clearOpacity();
10444                 (function(){
10445                     this.fxanim({
10446                         height:{to:1},
10447                         points:{by:[0, this.getHeight() * .5]}
10448                     }, o, 'motion', 0.3, 'easeIn', after);
10449                 }).defer(100, this);
10450             });
10451         });
10452         return this;
10453     },
10454
10455     /**
10456      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10457      * changed using the "attr" config option) and then fading back to the original color. If no original
10458      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10459      * Usage:
10460 <pre><code>
10461 // default: highlight background to yellow
10462 el.highlight();
10463
10464 // custom: highlight foreground text to blue for 2 seconds
10465 el.highlight("0000ff", { attr: 'color', duration: 2 });
10466
10467 // common config options shown with default values
10468 el.highlight("ffff9c", {
10469     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10470     endColor: (current color) or "ffffff",
10471     easing: 'easeIn',
10472     duration: 1
10473 });
10474 </code></pre>
10475      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10476      * @param {Object} options (optional) Object literal with any of the Fx config options
10477      * @return {Roo.Element} The Element
10478      */ 
10479     highlight : function(color, o){
10480         var el = this.getFxEl();
10481         o = o || {};
10482
10483         el.queueFx(o, function(){
10484             color = color || "ffff9c";
10485             attr = o.attr || "backgroundColor";
10486
10487             this.clearOpacity();
10488             this.show();
10489
10490             var origColor = this.getColor(attr);
10491             var restoreColor = this.dom.style[attr];
10492             endColor = (o.endColor || origColor) || "ffffff";
10493
10494             var after = function(){
10495                 el.dom.style[attr] = restoreColor;
10496                 el.afterFx(o);
10497             };
10498
10499             var a = {};
10500             a[attr] = {from: color, to: endColor};
10501             arguments.callee.anim = this.fxanim(a,
10502                 o,
10503                 'color',
10504                 1,
10505                 'easeIn', after);
10506         });
10507         return this;
10508     },
10509
10510    /**
10511     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10512     * Usage:
10513 <pre><code>
10514 // default: a single light blue ripple
10515 el.frame();
10516
10517 // custom: 3 red ripples lasting 3 seconds total
10518 el.frame("ff0000", 3, { duration: 3 });
10519
10520 // common config options shown with default values
10521 el.frame("C3DAF9", 1, {
10522     duration: 1 //duration of entire animation (not each individual ripple)
10523     // Note: Easing is not configurable and will be ignored if included
10524 });
10525 </code></pre>
10526     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10527     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10528     * @param {Object} options (optional) Object literal with any of the Fx config options
10529     * @return {Roo.Element} The Element
10530     */
10531     frame : function(color, count, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             color = color || "#C3DAF9";
10537             if(color.length == 6){
10538                 color = "#" + color;
10539             }
10540             count = count || 1;
10541             duration = o.duration || 1;
10542             this.show();
10543
10544             var b = this.getBox();
10545             var animFn = function(){
10546                 var proxy = this.createProxy({
10547
10548                      style:{
10549                         visbility:"hidden",
10550                         position:"absolute",
10551                         "z-index":"35000", // yee haw
10552                         border:"0px solid " + color
10553                      }
10554                   });
10555                 var scale = Roo.isBorderBox ? 2 : 1;
10556                 proxy.animate({
10557                     top:{from:b.y, to:b.y - 20},
10558                     left:{from:b.x, to:b.x - 20},
10559                     borderWidth:{from:0, to:10},
10560                     opacity:{from:1, to:0},
10561                     height:{from:b.height, to:(b.height + (20*scale))},
10562                     width:{from:b.width, to:(b.width + (20*scale))}
10563                 }, duration, function(){
10564                     proxy.remove();
10565                 });
10566                 if(--count > 0){
10567                      animFn.defer((duration/2)*1000, this);
10568                 }else{
10569                     el.afterFx(o);
10570                 }
10571             };
10572             animFn.call(this);
10573         });
10574         return this;
10575     },
10576
10577    /**
10578     * Creates a pause before any subsequent queued effects begin.  If there are
10579     * no effects queued after the pause it will have no effect.
10580     * Usage:
10581 <pre><code>
10582 el.pause(1);
10583 </code></pre>
10584     * @param {Number} seconds The length of time to pause (in seconds)
10585     * @return {Roo.Element} The Element
10586     */
10587     pause : function(seconds){
10588         var el = this.getFxEl();
10589         var o = {};
10590
10591         el.queueFx(o, function(){
10592             setTimeout(function(){
10593                 el.afterFx(o);
10594             }, seconds * 1000);
10595         });
10596         return this;
10597     },
10598
10599    /**
10600     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10601     * using the "endOpacity" config option.
10602     * Usage:
10603 <pre><code>
10604 // default: fade in from opacity 0 to 100%
10605 el.fadeIn();
10606
10607 // custom: fade in from opacity 0 to 75% over 2 seconds
10608 el.fadeIn({ endOpacity: .75, duration: 2});
10609
10610 // common config options shown with default values
10611 el.fadeIn({
10612     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10613     easing: 'easeOut',
10614     duration: .5
10615 });
10616 </code></pre>
10617     * @param {Object} options (optional) Object literal with any of the Fx config options
10618     * @return {Roo.Element} The Element
10619     */
10620     fadeIn : function(o){
10621         var el = this.getFxEl();
10622         o = o || {};
10623         el.queueFx(o, function(){
10624             this.setOpacity(0);
10625             this.fixDisplay();
10626             this.dom.style.visibility = 'visible';
10627             var to = o.endOpacity || 1;
10628             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10629                 o, null, .5, "easeOut", function(){
10630                 if(to == 1){
10631                     this.clearOpacity();
10632                 }
10633                 el.afterFx(o);
10634             });
10635         });
10636         return this;
10637     },
10638
10639    /**
10640     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10641     * using the "endOpacity" config option.
10642     * Usage:
10643 <pre><code>
10644 // default: fade out from the element's current opacity to 0
10645 el.fadeOut();
10646
10647 // custom: fade out from the element's current opacity to 25% over 2 seconds
10648 el.fadeOut({ endOpacity: .25, duration: 2});
10649
10650 // common config options shown with default values
10651 el.fadeOut({
10652     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10653     easing: 'easeOut',
10654     duration: .5
10655     remove: false,
10656     useDisplay: false
10657 });
10658 </code></pre>
10659     * @param {Object} options (optional) Object literal with any of the Fx config options
10660     * @return {Roo.Element} The Element
10661     */
10662     fadeOut : function(o){
10663         var el = this.getFxEl();
10664         o = o || {};
10665         el.queueFx(o, function(){
10666             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10667                 o, null, .5, "easeOut", function(){
10668                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10669                      this.dom.style.display = "none";
10670                 }else{
10671                      this.dom.style.visibility = "hidden";
10672                 }
10673                 this.clearOpacity();
10674                 el.afterFx(o);
10675             });
10676         });
10677         return this;
10678     },
10679
10680    /**
10681     * Animates the transition of an element's dimensions from a starting height/width
10682     * to an ending height/width.
10683     * Usage:
10684 <pre><code>
10685 // change height and width to 100x100 pixels
10686 el.scale(100, 100);
10687
10688 // common config options shown with default values.  The height and width will default to
10689 // the element's existing values if passed as null.
10690 el.scale(
10691     [element's width],
10692     [element's height], {
10693     easing: 'easeOut',
10694     duration: .35
10695 });
10696 </code></pre>
10697     * @param {Number} width  The new width (pass undefined to keep the original width)
10698     * @param {Number} height  The new height (pass undefined to keep the original height)
10699     * @param {Object} options (optional) Object literal with any of the Fx config options
10700     * @return {Roo.Element} The Element
10701     */
10702     scale : function(w, h, o){
10703         this.shift(Roo.apply({}, o, {
10704             width: w,
10705             height: h
10706         }));
10707         return this;
10708     },
10709
10710    /**
10711     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10712     * Any of these properties not specified in the config object will not be changed.  This effect 
10713     * requires that at least one new dimension, position or opacity setting must be passed in on
10714     * the config object in order for the function to have any effect.
10715     * Usage:
10716 <pre><code>
10717 // slide the element horizontally to x position 200 while changing the height and opacity
10718 el.shift({ x: 200, height: 50, opacity: .8 });
10719
10720 // common config options shown with default values.
10721 el.shift({
10722     width: [element's width],
10723     height: [element's height],
10724     x: [element's x position],
10725     y: [element's y position],
10726     opacity: [element's opacity],
10727     easing: 'easeOut',
10728     duration: .35
10729 });
10730 </code></pre>
10731     * @param {Object} options  Object literal with any of the Fx config options
10732     * @return {Roo.Element} The Element
10733     */
10734     shift : function(o){
10735         var el = this.getFxEl();
10736         o = o || {};
10737         el.queueFx(o, function(){
10738             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10739             if(w !== undefined){
10740                 a.width = {to: this.adjustWidth(w)};
10741             }
10742             if(h !== undefined){
10743                 a.height = {to: this.adjustHeight(h)};
10744             }
10745             if(x !== undefined || y !== undefined){
10746                 a.points = {to: [
10747                     x !== undefined ? x : this.getX(),
10748                     y !== undefined ? y : this.getY()
10749                 ]};
10750             }
10751             if(op !== undefined){
10752                 a.opacity = {to: op};
10753             }
10754             if(o.xy !== undefined){
10755                 a.points = {to: o.xy};
10756             }
10757             arguments.callee.anim = this.fxanim(a,
10758                 o, 'motion', .35, "easeOut", function(){
10759                 el.afterFx(o);
10760             });
10761         });
10762         return this;
10763     },
10764
10765         /**
10766          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10767          * ending point of the effect.
10768          * Usage:
10769          *<pre><code>
10770 // default: slide the element downward while fading out
10771 el.ghost();
10772
10773 // custom: slide the element out to the right with a 2-second duration
10774 el.ghost('r', { duration: 2 });
10775
10776 // common config options shown with default values
10777 el.ghost('b', {
10778     easing: 'easeOut',
10779     duration: .5
10780     remove: false,
10781     useDisplay: false
10782 });
10783 </code></pre>
10784          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10785          * @param {Object} options (optional) Object literal with any of the Fx config options
10786          * @return {Roo.Element} The Element
10787          */
10788     ghost : function(anchor, o){
10789         var el = this.getFxEl();
10790         o = o || {};
10791
10792         el.queueFx(o, function(){
10793             anchor = anchor || "b";
10794
10795             // restore values after effect
10796             var r = this.getFxRestore();
10797             var w = this.getWidth(),
10798                 h = this.getHeight();
10799
10800             var st = this.dom.style;
10801
10802             var after = function(){
10803                 if(o.useDisplay){
10804                     el.setDisplayed(false);
10805                 }else{
10806                     el.hide();
10807                 }
10808
10809                 el.clearOpacity();
10810                 el.setPositioning(r.pos);
10811                 st.width = r.width;
10812                 st.height = r.height;
10813
10814                 el.afterFx(o);
10815             };
10816
10817             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10818             switch(anchor.toLowerCase()){
10819                 case "t":
10820                     pt.by = [0, -h];
10821                 break;
10822                 case "l":
10823                     pt.by = [-w, 0];
10824                 break;
10825                 case "r":
10826                     pt.by = [w, 0];
10827                 break;
10828                 case "b":
10829                     pt.by = [0, h];
10830                 break;
10831                 case "tl":
10832                     pt.by = [-w, -h];
10833                 break;
10834                 case "bl":
10835                     pt.by = [-w, h];
10836                 break;
10837                 case "br":
10838                     pt.by = [w, h];
10839                 break;
10840                 case "tr":
10841                     pt.by = [w, -h];
10842                 break;
10843             }
10844
10845             arguments.callee.anim = this.fxanim(a,
10846                 o,
10847                 'motion',
10848                 .5,
10849                 "easeOut", after);
10850         });
10851         return this;
10852     },
10853
10854         /**
10855          * Ensures that all effects queued after syncFx is called on the element are
10856          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10857          * @return {Roo.Element} The Element
10858          */
10859     syncFx : function(){
10860         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10861             block : false,
10862             concurrent : true,
10863             stopFx : false
10864         });
10865         return this;
10866     },
10867
10868         /**
10869          * Ensures that all effects queued after sequenceFx is called on the element are
10870          * run in sequence.  This is the opposite of {@link #syncFx}.
10871          * @return {Roo.Element} The Element
10872          */
10873     sequenceFx : function(){
10874         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10875             block : false,
10876             concurrent : false,
10877             stopFx : false
10878         });
10879         return this;
10880     },
10881
10882         /* @private */
10883     nextFx : function(){
10884         var ef = this.fxQueue[0];
10885         if(ef){
10886             ef.call(this);
10887         }
10888     },
10889
10890         /**
10891          * Returns true if the element has any effects actively running or queued, else returns false.
10892          * @return {Boolean} True if element has active effects, else false
10893          */
10894     hasActiveFx : function(){
10895         return this.fxQueue && this.fxQueue[0];
10896     },
10897
10898         /**
10899          * Stops any running effects and clears the element's internal effects queue if it contains
10900          * any additional effects that haven't started yet.
10901          * @return {Roo.Element} The Element
10902          */
10903     stopFx : function(){
10904         if(this.hasActiveFx()){
10905             var cur = this.fxQueue[0];
10906             if(cur && cur.anim && cur.anim.isAnimated()){
10907                 this.fxQueue = [cur]; // clear out others
10908                 cur.anim.stop(true);
10909             }
10910         }
10911         return this;
10912     },
10913
10914         /* @private */
10915     beforeFx : function(o){
10916         if(this.hasActiveFx() && !o.concurrent){
10917            if(o.stopFx){
10918                this.stopFx();
10919                return true;
10920            }
10921            return false;
10922         }
10923         return true;
10924     },
10925
10926         /**
10927          * Returns true if the element is currently blocking so that no other effect can be queued
10928          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10929          * used to ensure that an effect initiated by a user action runs to completion prior to the
10930          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10931          * @return {Boolean} True if blocking, else false
10932          */
10933     hasFxBlock : function(){
10934         var q = this.fxQueue;
10935         return q && q[0] && q[0].block;
10936     },
10937
10938         /* @private */
10939     queueFx : function(o, fn){
10940         if(!this.fxQueue){
10941             this.fxQueue = [];
10942         }
10943         if(!this.hasFxBlock()){
10944             Roo.applyIf(o, this.fxDefaults);
10945             if(!o.concurrent){
10946                 var run = this.beforeFx(o);
10947                 fn.block = o.block;
10948                 this.fxQueue.push(fn);
10949                 if(run){
10950                     this.nextFx();
10951                 }
10952             }else{
10953                 fn.call(this);
10954             }
10955         }
10956         return this;
10957     },
10958
10959         /* @private */
10960     fxWrap : function(pos, o, vis){
10961         var wrap;
10962         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10963             var wrapXY;
10964             if(o.fixPosition){
10965                 wrapXY = this.getXY();
10966             }
10967             var div = document.createElement("div");
10968             div.style.visibility = vis;
10969             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10970             wrap.setPositioning(pos);
10971             if(wrap.getStyle("position") == "static"){
10972                 wrap.position("relative");
10973             }
10974             this.clearPositioning('auto');
10975             wrap.clip();
10976             wrap.dom.appendChild(this.dom);
10977             if(wrapXY){
10978                 wrap.setXY(wrapXY);
10979             }
10980         }
10981         return wrap;
10982     },
10983
10984         /* @private */
10985     fxUnwrap : function(wrap, pos, o){
10986         this.clearPositioning();
10987         this.setPositioning(pos);
10988         if(!o.wrap){
10989             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10990             wrap.remove();
10991         }
10992     },
10993
10994         /* @private */
10995     getFxRestore : function(){
10996         var st = this.dom.style;
10997         return {pos: this.getPositioning(), width: st.width, height : st.height};
10998     },
10999
11000         /* @private */
11001     afterFx : function(o){
11002         if(o.afterStyle){
11003             this.applyStyles(o.afterStyle);
11004         }
11005         if(o.afterCls){
11006             this.addClass(o.afterCls);
11007         }
11008         if(o.remove === true){
11009             this.remove();
11010         }
11011         Roo.callback(o.callback, o.scope, [this]);
11012         if(!o.concurrent){
11013             this.fxQueue.shift();
11014             this.nextFx();
11015         }
11016     },
11017
11018         /* @private */
11019     getFxEl : function(){ // support for composite element fx
11020         return Roo.get(this.dom);
11021     },
11022
11023         /* @private */
11024     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11025         animType = animType || 'run';
11026         opt = opt || {};
11027         var anim = Roo.lib.Anim[animType](
11028             this.dom, args,
11029             (opt.duration || defaultDur) || .35,
11030             (opt.easing || defaultEase) || 'easeOut',
11031             function(){
11032                 Roo.callback(cb, this);
11033             },
11034             this
11035         );
11036         opt.anim = anim;
11037         return anim;
11038     }
11039 };
11040
11041 // backwords compat
11042 Roo.Fx.resize = Roo.Fx.scale;
11043
11044 //When included, Roo.Fx is automatically applied to Element so that all basic
11045 //effects are available directly via the Element API
11046 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11047  * Based on:
11048  * Ext JS Library 1.1.1
11049  * Copyright(c) 2006-2007, Ext JS, LLC.
11050  *
11051  * Originally Released Under LGPL - original licence link has changed is not relivant.
11052  *
11053  * Fork - LGPL
11054  * <script type="text/javascript">
11055  */
11056
11057
11058 /**
11059  * @class Roo.CompositeElement
11060  * Standard composite class. Creates a Roo.Element for every element in the collection.
11061  * <br><br>
11062  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11063  * actions will be performed on all the elements in this collection.</b>
11064  * <br><br>
11065  * All methods return <i>this</i> and can be chained.
11066  <pre><code>
11067  var els = Roo.select("#some-el div.some-class", true);
11068  // or select directly from an existing element
11069  var el = Roo.get('some-el');
11070  el.select('div.some-class', true);
11071
11072  els.setWidth(100); // all elements become 100 width
11073  els.hide(true); // all elements fade out and hide
11074  // or
11075  els.setWidth(100).hide(true);
11076  </code></pre>
11077  */
11078 Roo.CompositeElement = function(els){
11079     this.elements = [];
11080     this.addElements(els);
11081 };
11082 Roo.CompositeElement.prototype = {
11083     isComposite: true,
11084     addElements : function(els){
11085         if(!els) {
11086             return this;
11087         }
11088         if(typeof els == "string"){
11089             els = Roo.Element.selectorFunction(els);
11090         }
11091         var yels = this.elements;
11092         var index = yels.length-1;
11093         for(var i = 0, len = els.length; i < len; i++) {
11094                 yels[++index] = Roo.get(els[i]);
11095         }
11096         return this;
11097     },
11098
11099     /**
11100     * Clears this composite and adds the elements returned by the passed selector.
11101     * @param {String/Array} els A string CSS selector, an array of elements or an element
11102     * @return {CompositeElement} this
11103     */
11104     fill : function(els){
11105         this.elements = [];
11106         this.add(els);
11107         return this;
11108     },
11109
11110     /**
11111     * Filters this composite to only elements that match the passed selector.
11112     * @param {String} selector A string CSS selector
11113     * @param {Boolean} inverse return inverse filter (not matches)
11114     * @return {CompositeElement} this
11115     */
11116     filter : function(selector, inverse){
11117         var els = [];
11118         inverse = inverse || false;
11119         this.each(function(el){
11120             var match = inverse ? !el.is(selector) : el.is(selector);
11121             if(match){
11122                 els[els.length] = el.dom;
11123             }
11124         });
11125         this.fill(els);
11126         return this;
11127     },
11128
11129     invoke : function(fn, args){
11130         var els = this.elements;
11131         for(var i = 0, len = els.length; i < len; i++) {
11132                 Roo.Element.prototype[fn].apply(els[i], args);
11133         }
11134         return this;
11135     },
11136     /**
11137     * Adds elements to this composite.
11138     * @param {String/Array} els A string CSS selector, an array of elements or an element
11139     * @return {CompositeElement} this
11140     */
11141     add : function(els){
11142         if(typeof els == "string"){
11143             this.addElements(Roo.Element.selectorFunction(els));
11144         }else if(els.length !== undefined){
11145             this.addElements(els);
11146         }else{
11147             this.addElements([els]);
11148         }
11149         return this;
11150     },
11151     /**
11152     * Calls the passed function passing (el, this, index) for each element in this composite.
11153     * @param {Function} fn The function to call
11154     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11155     * @return {CompositeElement} this
11156     */
11157     each : function(fn, scope){
11158         var els = this.elements;
11159         for(var i = 0, len = els.length; i < len; i++){
11160             if(fn.call(scope || els[i], els[i], this, i) === false) {
11161                 break;
11162             }
11163         }
11164         return this;
11165     },
11166
11167     /**
11168      * Returns the Element object at the specified index
11169      * @param {Number} index
11170      * @return {Roo.Element}
11171      */
11172     item : function(index){
11173         return this.elements[index] || null;
11174     },
11175
11176     /**
11177      * Returns the first Element
11178      * @return {Roo.Element}
11179      */
11180     first : function(){
11181         return this.item(0);
11182     },
11183
11184     /**
11185      * Returns the last Element
11186      * @return {Roo.Element}
11187      */
11188     last : function(){
11189         return this.item(this.elements.length-1);
11190     },
11191
11192     /**
11193      * Returns the number of elements in this composite
11194      * @return Number
11195      */
11196     getCount : function(){
11197         return this.elements.length;
11198     },
11199
11200     /**
11201      * Returns true if this composite contains the passed element
11202      * @return Boolean
11203      */
11204     contains : function(el){
11205         return this.indexOf(el) !== -1;
11206     },
11207
11208     /**
11209      * Returns true if this composite contains the passed element
11210      * @return Boolean
11211      */
11212     indexOf : function(el){
11213         return this.elements.indexOf(Roo.get(el));
11214     },
11215
11216
11217     /**
11218     * Removes the specified element(s).
11219     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11220     * or an array of any of those.
11221     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11222     * @return {CompositeElement} this
11223     */
11224     removeElement : function(el, removeDom){
11225         if(el instanceof Array){
11226             for(var i = 0, len = el.length; i < len; i++){
11227                 this.removeElement(el[i]);
11228             }
11229             return this;
11230         }
11231         var index = typeof el == 'number' ? el : this.indexOf(el);
11232         if(index !== -1){
11233             if(removeDom){
11234                 var d = this.elements[index];
11235                 if(d.dom){
11236                     d.remove();
11237                 }else{
11238                     d.parentNode.removeChild(d);
11239                 }
11240             }
11241             this.elements.splice(index, 1);
11242         }
11243         return this;
11244     },
11245
11246     /**
11247     * Replaces the specified element with the passed element.
11248     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11249     * to replace.
11250     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11251     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11252     * @return {CompositeElement} this
11253     */
11254     replaceElement : function(el, replacement, domReplace){
11255         var index = typeof el == 'number' ? el : this.indexOf(el);
11256         if(index !== -1){
11257             if(domReplace){
11258                 this.elements[index].replaceWith(replacement);
11259             }else{
11260                 this.elements.splice(index, 1, Roo.get(replacement))
11261             }
11262         }
11263         return this;
11264     },
11265
11266     /**
11267      * Removes all elements.
11268      */
11269     clear : function(){
11270         this.elements = [];
11271     }
11272 };
11273 (function(){
11274     Roo.CompositeElement.createCall = function(proto, fnName){
11275         if(!proto[fnName]){
11276             proto[fnName] = function(){
11277                 return this.invoke(fnName, arguments);
11278             };
11279         }
11280     };
11281     for(var fnName in Roo.Element.prototype){
11282         if(typeof Roo.Element.prototype[fnName] == "function"){
11283             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11284         }
11285     };
11286 })();
11287 /*
11288  * Based on:
11289  * Ext JS Library 1.1.1
11290  * Copyright(c) 2006-2007, Ext JS, LLC.
11291  *
11292  * Originally Released Under LGPL - original licence link has changed is not relivant.
11293  *
11294  * Fork - LGPL
11295  * <script type="text/javascript">
11296  */
11297
11298 /**
11299  * @class Roo.CompositeElementLite
11300  * @extends Roo.CompositeElement
11301  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11302  <pre><code>
11303  var els = Roo.select("#some-el div.some-class");
11304  // or select directly from an existing element
11305  var el = Roo.get('some-el');
11306  el.select('div.some-class');
11307
11308  els.setWidth(100); // all elements become 100 width
11309  els.hide(true); // all elements fade out and hide
11310  // or
11311  els.setWidth(100).hide(true);
11312  </code></pre><br><br>
11313  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11314  * actions will be performed on all the elements in this collection.</b>
11315  */
11316 Roo.CompositeElementLite = function(els){
11317     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11318     this.el = new Roo.Element.Flyweight();
11319 };
11320 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11321     addElements : function(els){
11322         if(els){
11323             if(els instanceof Array){
11324                 this.elements = this.elements.concat(els);
11325             }else{
11326                 var yels = this.elements;
11327                 var index = yels.length-1;
11328                 for(var i = 0, len = els.length; i < len; i++) {
11329                     yels[++index] = els[i];
11330                 }
11331             }
11332         }
11333         return this;
11334     },
11335     invoke : function(fn, args){
11336         var els = this.elements;
11337         var el = this.el;
11338         for(var i = 0, len = els.length; i < len; i++) {
11339             el.dom = els[i];
11340                 Roo.Element.prototype[fn].apply(el, args);
11341         }
11342         return this;
11343     },
11344     /**
11345      * Returns a flyweight Element of the dom element object at the specified index
11346      * @param {Number} index
11347      * @return {Roo.Element}
11348      */
11349     item : function(index){
11350         if(!this.elements[index]){
11351             return null;
11352         }
11353         this.el.dom = this.elements[index];
11354         return this.el;
11355     },
11356
11357     // fixes scope with flyweight
11358     addListener : function(eventName, handler, scope, opt){
11359         var els = this.elements;
11360         for(var i = 0, len = els.length; i < len; i++) {
11361             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11362         }
11363         return this;
11364     },
11365
11366     /**
11367     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11368     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11369     * a reference to the dom node, use el.dom.</b>
11370     * @param {Function} fn The function to call
11371     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11372     * @return {CompositeElement} this
11373     */
11374     each : function(fn, scope){
11375         var els = this.elements;
11376         var el = this.el;
11377         for(var i = 0, len = els.length; i < len; i++){
11378             el.dom = els[i];
11379                 if(fn.call(scope || el, el, this, i) === false){
11380                 break;
11381             }
11382         }
11383         return this;
11384     },
11385
11386     indexOf : function(el){
11387         return this.elements.indexOf(Roo.getDom(el));
11388     },
11389
11390     replaceElement : function(el, replacement, domReplace){
11391         var index = typeof el == 'number' ? el : this.indexOf(el);
11392         if(index !== -1){
11393             replacement = Roo.getDom(replacement);
11394             if(domReplace){
11395                 var d = this.elements[index];
11396                 d.parentNode.insertBefore(replacement, d);
11397                 d.parentNode.removeChild(d);
11398             }
11399             this.elements.splice(index, 1, replacement);
11400         }
11401         return this;
11402     }
11403 });
11404 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11405
11406 /*
11407  * Based on:
11408  * Ext JS Library 1.1.1
11409  * Copyright(c) 2006-2007, Ext JS, LLC.
11410  *
11411  * Originally Released Under LGPL - original licence link has changed is not relivant.
11412  *
11413  * Fork - LGPL
11414  * <script type="text/javascript">
11415  */
11416
11417  
11418
11419 /**
11420  * @class Roo.data.Connection
11421  * @extends Roo.util.Observable
11422  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11423  * either to a configured URL, or to a URL specified at request time.<br><br>
11424  * <p>
11425  * Requests made by this class are asynchronous, and will return immediately. No data from
11426  * the server will be available to the statement immediately following the {@link #request} call.
11427  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11428  * <p>
11429  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11430  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11431  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11432  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11433  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11434  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11435  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11436  * standard DOM methods.
11437  * @constructor
11438  * @param {Object} config a configuration object.
11439  */
11440 Roo.data.Connection = function(config){
11441     Roo.apply(this, config);
11442     this.addEvents({
11443         /**
11444          * @event beforerequest
11445          * Fires before a network request is made to retrieve a data object.
11446          * @param {Connection} conn This Connection object.
11447          * @param {Object} options The options config object passed to the {@link #request} method.
11448          */
11449         "beforerequest" : true,
11450         /**
11451          * @event requestcomplete
11452          * Fires if the request was successfully completed.
11453          * @param {Connection} conn This Connection object.
11454          * @param {Object} response The XHR object containing the response data.
11455          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11456          * @param {Object} options The options config object passed to the {@link #request} method.
11457          */
11458         "requestcomplete" : true,
11459         /**
11460          * @event requestexception
11461          * Fires if an error HTTP status was returned from the server.
11462          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11463          * @param {Connection} conn This Connection object.
11464          * @param {Object} response The XHR object containing the response data.
11465          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11466          * @param {Object} options The options config object passed to the {@link #request} method.
11467          */
11468         "requestexception" : true
11469     });
11470     Roo.data.Connection.superclass.constructor.call(this);
11471 };
11472
11473 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11474     /**
11475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11476      */
11477     /**
11478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11479      * extra parameters to each request made by this object. (defaults to undefined)
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11483      *  to each request made by this object. (defaults to undefined)
11484      */
11485     /**
11486      * @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)
11487      */
11488     /**
11489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11490      */
11491     timeout : 30000,
11492     /**
11493      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11494      * @type Boolean
11495      */
11496     autoAbort:false,
11497
11498     /**
11499      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11500      * @type Boolean
11501      */
11502     disableCaching: true,
11503
11504     /**
11505      * Sends an HTTP request to a remote server.
11506      * @param {Object} options An object which may contain the following properties:<ul>
11507      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11508      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11509      * request, a url encoded string or a function to call to get either.</li>
11510      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11511      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11512      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11513      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11514      * <li>options {Object} The parameter to the request call.</li>
11515      * <li>success {Boolean} True if the request succeeded.</li>
11516      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11517      * </ul></li>
11518      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11519      * The callback is passed the following parameters:<ul>
11520      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11521      * <li>options {Object} The parameter to the request call.</li>
11522      * </ul></li>
11523      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11524      * The callback is passed the following parameters:<ul>
11525      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11526      * <li>options {Object} The parameter to the request call.</li>
11527      * </ul></li>
11528      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11529      * for the callback function. Defaults to the browser window.</li>
11530      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11531      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11532      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11533      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11534      * params for the post data. Any params will be appended to the URL.</li>
11535      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11536      * </ul>
11537      * @return {Number} transactionId
11538      */
11539     request : function(o){
11540         if(this.fireEvent("beforerequest", this, o) !== false){
11541             var p = o.params;
11542
11543             if(typeof p == "function"){
11544                 p = p.call(o.scope||window, o);
11545             }
11546             if(typeof p == "object"){
11547                 p = Roo.urlEncode(o.params);
11548             }
11549             if(this.extraParams){
11550                 var extras = Roo.urlEncode(this.extraParams);
11551                 p = p ? (p + '&' + extras) : extras;
11552             }
11553
11554             var url = o.url || this.url;
11555             if(typeof url == 'function'){
11556                 url = url.call(o.scope||window, o);
11557             }
11558
11559             if(o.form){
11560                 var form = Roo.getDom(o.form);
11561                 url = url || form.action;
11562
11563                 var enctype = form.getAttribute("enctype");
11564                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11565                     return this.doFormUpload(o, p, url);
11566                 }
11567                 var f = Roo.lib.Ajax.serializeForm(form);
11568                 p = p ? (p + '&' + f) : f;
11569             }
11570
11571             var hs = o.headers;
11572             if(this.defaultHeaders){
11573                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11574                 if(!o.headers){
11575                     o.headers = hs;
11576                 }
11577             }
11578
11579             var cb = {
11580                 success: this.handleResponse,
11581                 failure: this.handleFailure,
11582                 scope: this,
11583                 argument: {options: o},
11584                 timeout : o.timeout || this.timeout
11585             };
11586
11587             var method = o.method||this.method||(p ? "POST" : "GET");
11588
11589             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11590                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11591             }
11592
11593             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11594                 if(o.autoAbort){
11595                     this.abort();
11596                 }
11597             }else if(this.autoAbort !== false){
11598                 this.abort();
11599             }
11600
11601             if((method == 'GET' && p) || o.xmlData){
11602                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11603                 p = '';
11604             }
11605             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11606             return this.transId;
11607         }else{
11608             Roo.callback(o.callback, o.scope, [o, null, null]);
11609             return null;
11610         }
11611     },
11612
11613     /**
11614      * Determine whether this object has a request outstanding.
11615      * @param {Number} transactionId (Optional) defaults to the last transaction
11616      * @return {Boolean} True if there is an outstanding request.
11617      */
11618     isLoading : function(transId){
11619         if(transId){
11620             return Roo.lib.Ajax.isCallInProgress(transId);
11621         }else{
11622             return this.transId ? true : false;
11623         }
11624     },
11625
11626     /**
11627      * Aborts any outstanding request.
11628      * @param {Number} transactionId (Optional) defaults to the last transaction
11629      */
11630     abort : function(transId){
11631         if(transId || this.isLoading()){
11632             Roo.lib.Ajax.abort(transId || this.transId);
11633         }
11634     },
11635
11636     // private
11637     handleResponse : function(response){
11638         this.transId = false;
11639         var options = response.argument.options;
11640         response.argument = options ? options.argument : null;
11641         this.fireEvent("requestcomplete", this, response, options);
11642         Roo.callback(options.success, options.scope, [response, options]);
11643         Roo.callback(options.callback, options.scope, [options, true, response]);
11644     },
11645
11646     // private
11647     handleFailure : function(response, e){
11648         this.transId = false;
11649         var options = response.argument.options;
11650         response.argument = options ? options.argument : null;
11651         this.fireEvent("requestexception", this, response, options, e);
11652         Roo.callback(options.failure, options.scope, [response, options]);
11653         Roo.callback(options.callback, options.scope, [options, false, response]);
11654     },
11655
11656     // private
11657     doFormUpload : function(o, ps, url){
11658         var id = Roo.id();
11659         var frame = document.createElement('iframe');
11660         frame.id = id;
11661         frame.name = id;
11662         frame.className = 'x-hidden';
11663         if(Roo.isIE){
11664             frame.src = Roo.SSL_SECURE_URL;
11665         }
11666         document.body.appendChild(frame);
11667
11668         if(Roo.isIE){
11669            document.frames[id].name = id;
11670         }
11671
11672         var form = Roo.getDom(o.form);
11673         form.target = id;
11674         form.method = 'POST';
11675         form.enctype = form.encoding = 'multipart/form-data';
11676         if(url){
11677             form.action = url;
11678         }
11679
11680         var hiddens, hd;
11681         if(ps){ // add dynamic params
11682             hiddens = [];
11683             ps = Roo.urlDecode(ps, false);
11684             for(var k in ps){
11685                 if(ps.hasOwnProperty(k)){
11686                     hd = document.createElement('input');
11687                     hd.type = 'hidden';
11688                     hd.name = k;
11689                     hd.value = ps[k];
11690                     form.appendChild(hd);
11691                     hiddens.push(hd);
11692                 }
11693             }
11694         }
11695
11696         function cb(){
11697             var r = {  // bogus response object
11698                 responseText : '',
11699                 responseXML : null
11700             };
11701
11702             r.argument = o ? o.argument : null;
11703
11704             try { //
11705                 var doc;
11706                 if(Roo.isIE){
11707                     doc = frame.contentWindow.document;
11708                 }else {
11709                     doc = (frame.contentDocument || window.frames[id].document);
11710                 }
11711                 if(doc && doc.body){
11712                     r.responseText = doc.body.innerHTML;
11713                 }
11714                 if(doc && doc.XMLDocument){
11715                     r.responseXML = doc.XMLDocument;
11716                 }else {
11717                     r.responseXML = doc;
11718                 }
11719             }
11720             catch(e) {
11721                 // ignore
11722             }
11723
11724             Roo.EventManager.removeListener(frame, 'load', cb, this);
11725
11726             this.fireEvent("requestcomplete", this, r, o);
11727             Roo.callback(o.success, o.scope, [r, o]);
11728             Roo.callback(o.callback, o.scope, [o, true, r]);
11729
11730             setTimeout(function(){document.body.removeChild(frame);}, 100);
11731         }
11732
11733         Roo.EventManager.on(frame, 'load', cb, this);
11734         form.submit();
11735
11736         if(hiddens){ // remove dynamic params
11737             for(var i = 0, len = hiddens.length; i < len; i++){
11738                 form.removeChild(hiddens[i]);
11739             }
11740         }
11741     }
11742 });
11743 /*
11744  * Based on:
11745  * Ext JS Library 1.1.1
11746  * Copyright(c) 2006-2007, Ext JS, LLC.
11747  *
11748  * Originally Released Under LGPL - original licence link has changed is not relivant.
11749  *
11750  * Fork - LGPL
11751  * <script type="text/javascript">
11752  */
11753  
11754 /**
11755  * Global Ajax request class.
11756  * 
11757  * @class Roo.Ajax
11758  * @extends Roo.data.Connection
11759  * @static
11760  * 
11761  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11762  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11763  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11764  * @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)
11765  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11766  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11767  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11768  */
11769 Roo.Ajax = new Roo.data.Connection({
11770     // fix up the docs
11771     /**
11772      * @scope Roo.Ajax
11773      * @type {Boolear} 
11774      */
11775     autoAbort : false,
11776
11777     /**
11778      * Serialize the passed form into a url encoded string
11779      * @scope Roo.Ajax
11780      * @param {String/HTMLElement} form
11781      * @return {String}
11782      */
11783     serializeForm : function(form){
11784         return Roo.lib.Ajax.serializeForm(form);
11785     }
11786 });/*
11787  * Based on:
11788  * Ext JS Library 1.1.1
11789  * Copyright(c) 2006-2007, Ext JS, LLC.
11790  *
11791  * Originally Released Under LGPL - original licence link has changed is not relivant.
11792  *
11793  * Fork - LGPL
11794  * <script type="text/javascript">
11795  */
11796
11797  
11798 /**
11799  * @class Roo.UpdateManager
11800  * @extends Roo.util.Observable
11801  * Provides AJAX-style update for Element object.<br><br>
11802  * Usage:<br>
11803  * <pre><code>
11804  * // Get it from a Roo.Element object
11805  * var el = Roo.get("foo");
11806  * var mgr = el.getUpdateManager();
11807  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11808  * ...
11809  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11810  * <br>
11811  * // or directly (returns the same UpdateManager instance)
11812  * var mgr = new Roo.UpdateManager("myElementId");
11813  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11814  * mgr.on("update", myFcnNeedsToKnow);
11815  * <br>
11816    // short handed call directly from the element object
11817    Roo.get("foo").load({
11818         url: "bar.php",
11819         scripts:true,
11820         params: "for=bar",
11821         text: "Loading Foo..."
11822    });
11823  * </code></pre>
11824  * @constructor
11825  * Create new UpdateManager directly.
11826  * @param {String/HTMLElement/Roo.Element} el The element to update
11827  * @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).
11828  */
11829 Roo.UpdateManager = function(el, forceNew){
11830     el = Roo.get(el);
11831     if(!forceNew && el.updateManager){
11832         return el.updateManager;
11833     }
11834     /**
11835      * The Element object
11836      * @type Roo.Element
11837      */
11838     this.el = el;
11839     /**
11840      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11841      * @type String
11842      */
11843     this.defaultUrl = null;
11844
11845     this.addEvents({
11846         /**
11847          * @event beforeupdate
11848          * Fired before an update is made, return false from your handler and the update is cancelled.
11849          * @param {Roo.Element} el
11850          * @param {String/Object/Function} url
11851          * @param {String/Object} params
11852          */
11853         "beforeupdate": true,
11854         /**
11855          * @event update
11856          * Fired after successful update is made.
11857          * @param {Roo.Element} el
11858          * @param {Object} oResponseObject The response Object
11859          */
11860         "update": true,
11861         /**
11862          * @event failure
11863          * Fired on update failure.
11864          * @param {Roo.Element} el
11865          * @param {Object} oResponseObject The response Object
11866          */
11867         "failure": true
11868     });
11869     var d = Roo.UpdateManager.defaults;
11870     /**
11871      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11872      * @type String
11873      */
11874     this.sslBlankUrl = d.sslBlankUrl;
11875     /**
11876      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11877      * @type Boolean
11878      */
11879     this.disableCaching = d.disableCaching;
11880     /**
11881      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11882      * @type String
11883      */
11884     this.indicatorText = d.indicatorText;
11885     /**
11886      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11887      * @type String
11888      */
11889     this.showLoadIndicator = d.showLoadIndicator;
11890     /**
11891      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11892      * @type Number
11893      */
11894     this.timeout = d.timeout;
11895
11896     /**
11897      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11898      * @type Boolean
11899      */
11900     this.loadScripts = d.loadScripts;
11901
11902     /**
11903      * Transaction object of current executing transaction
11904      */
11905     this.transaction = null;
11906
11907     /**
11908      * @private
11909      */
11910     this.autoRefreshProcId = null;
11911     /**
11912      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11913      * @type Function
11914      */
11915     this.refreshDelegate = this.refresh.createDelegate(this);
11916     /**
11917      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11918      * @type Function
11919      */
11920     this.updateDelegate = this.update.createDelegate(this);
11921     /**
11922      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11923      * @type Function
11924      */
11925     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11926     /**
11927      * @private
11928      */
11929     this.successDelegate = this.processSuccess.createDelegate(this);
11930     /**
11931      * @private
11932      */
11933     this.failureDelegate = this.processFailure.createDelegate(this);
11934
11935     if(!this.renderer){
11936      /**
11937       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11938       */
11939     this.renderer = new Roo.UpdateManager.BasicRenderer();
11940     }
11941     
11942     Roo.UpdateManager.superclass.constructor.call(this);
11943 };
11944
11945 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11946     /**
11947      * Get the Element this UpdateManager is bound to
11948      * @return {Roo.Element} The element
11949      */
11950     getEl : function(){
11951         return this.el;
11952     },
11953     /**
11954      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11955      * @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:
11956 <pre><code>
11957 um.update({<br/>
11958     url: "your-url.php",<br/>
11959     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11960     callback: yourFunction,<br/>
11961     scope: yourObject, //(optional scope)  <br/>
11962     discardUrl: false, <br/>
11963     nocache: false,<br/>
11964     text: "Loading...",<br/>
11965     timeout: 30,<br/>
11966     scripts: false<br/>
11967 });
11968 </code></pre>
11969      * The only required property is url. The optional properties nocache, text and scripts
11970      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11971      * @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}
11972      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11973      * @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.
11974      */
11975     update : function(url, params, callback, discardUrl){
11976         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11977             var method = this.method,
11978                 cfg;
11979             if(typeof url == "object"){ // must be config object
11980                 cfg = url;
11981                 url = cfg.url;
11982                 params = params || cfg.params;
11983                 callback = callback || cfg.callback;
11984                 discardUrl = discardUrl || cfg.discardUrl;
11985                 if(callback && cfg.scope){
11986                     callback = callback.createDelegate(cfg.scope);
11987                 }
11988                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11989                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11990                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11991                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11992                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11993             }
11994             this.showLoading();
11995             if(!discardUrl){
11996                 this.defaultUrl = url;
11997             }
11998             if(typeof url == "function"){
11999                 url = url.call(this);
12000             }
12001
12002             method = method || (params ? "POST" : "GET");
12003             if(method == "GET"){
12004                 url = this.prepareUrl(url);
12005             }
12006
12007             var o = Roo.apply(cfg ||{}, {
12008                 url : url,
12009                 params: params,
12010                 success: this.successDelegate,
12011                 failure: this.failureDelegate,
12012                 callback: undefined,
12013                 timeout: (this.timeout*1000),
12014                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12015             });
12016             Roo.log("updated manager called with timeout of " + o.timeout);
12017             this.transaction = Roo.Ajax.request(o);
12018         }
12019     },
12020
12021     /**
12022      * 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.
12023      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12024      * @param {String/HTMLElement} form The form Id or form element
12025      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12026      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12027      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12028      */
12029     formUpdate : function(form, url, reset, callback){
12030         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12031             if(typeof url == "function"){
12032                 url = url.call(this);
12033             }
12034             form = Roo.getDom(form);
12035             this.transaction = Roo.Ajax.request({
12036                 form: form,
12037                 url:url,
12038                 success: this.successDelegate,
12039                 failure: this.failureDelegate,
12040                 timeout: (this.timeout*1000),
12041                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12042             });
12043             this.showLoading.defer(1, this);
12044         }
12045     },
12046
12047     /**
12048      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12049      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12050      */
12051     refresh : function(callback){
12052         if(this.defaultUrl == null){
12053             return;
12054         }
12055         this.update(this.defaultUrl, null, callback, true);
12056     },
12057
12058     /**
12059      * Set this element to auto refresh.
12060      * @param {Number} interval How often to update (in seconds).
12061      * @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)
12062      * @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}
12063      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12064      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12065      */
12066     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12067         if(refreshNow){
12068             this.update(url || this.defaultUrl, params, callback, true);
12069         }
12070         if(this.autoRefreshProcId){
12071             clearInterval(this.autoRefreshProcId);
12072         }
12073         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12074     },
12075
12076     /**
12077      * Stop auto refresh on this element.
12078      */
12079      stopAutoRefresh : function(){
12080         if(this.autoRefreshProcId){
12081             clearInterval(this.autoRefreshProcId);
12082             delete this.autoRefreshProcId;
12083         }
12084     },
12085
12086     isAutoRefreshing : function(){
12087        return this.autoRefreshProcId ? true : false;
12088     },
12089     /**
12090      * Called to update the element to "Loading" state. Override to perform custom action.
12091      */
12092     showLoading : function(){
12093         if(this.showLoadIndicator){
12094             this.el.update(this.indicatorText);
12095         }
12096     },
12097
12098     /**
12099      * Adds unique parameter to query string if disableCaching = true
12100      * @private
12101      */
12102     prepareUrl : function(url){
12103         if(this.disableCaching){
12104             var append = "_dc=" + (new Date().getTime());
12105             if(url.indexOf("?") !== -1){
12106                 url += "&" + append;
12107             }else{
12108                 url += "?" + append;
12109             }
12110         }
12111         return url;
12112     },
12113
12114     /**
12115      * @private
12116      */
12117     processSuccess : function(response){
12118         this.transaction = null;
12119         if(response.argument.form && response.argument.reset){
12120             try{ // put in try/catch since some older FF releases had problems with this
12121                 response.argument.form.reset();
12122             }catch(e){}
12123         }
12124         if(this.loadScripts){
12125             this.renderer.render(this.el, response, this,
12126                 this.updateComplete.createDelegate(this, [response]));
12127         }else{
12128             this.renderer.render(this.el, response, this);
12129             this.updateComplete(response);
12130         }
12131     },
12132
12133     updateComplete : function(response){
12134         this.fireEvent("update", this.el, response);
12135         if(typeof response.argument.callback == "function"){
12136             response.argument.callback(this.el, true, response);
12137         }
12138     },
12139
12140     /**
12141      * @private
12142      */
12143     processFailure : function(response){
12144         this.transaction = null;
12145         this.fireEvent("failure", this.el, response);
12146         if(typeof response.argument.callback == "function"){
12147             response.argument.callback(this.el, false, response);
12148         }
12149     },
12150
12151     /**
12152      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12153      * @param {Object} renderer The object implementing the render() method
12154      */
12155     setRenderer : function(renderer){
12156         this.renderer = renderer;
12157     },
12158
12159     getRenderer : function(){
12160        return this.renderer;
12161     },
12162
12163     /**
12164      * Set the defaultUrl used for updates
12165      * @param {String/Function} defaultUrl The url or a function to call to get the url
12166      */
12167     setDefaultUrl : function(defaultUrl){
12168         this.defaultUrl = defaultUrl;
12169     },
12170
12171     /**
12172      * Aborts the executing transaction
12173      */
12174     abort : function(){
12175         if(this.transaction){
12176             Roo.Ajax.abort(this.transaction);
12177         }
12178     },
12179
12180     /**
12181      * Returns true if an update is in progress
12182      * @return {Boolean}
12183      */
12184     isUpdating : function(){
12185         if(this.transaction){
12186             return Roo.Ajax.isLoading(this.transaction);
12187         }
12188         return false;
12189     }
12190 });
12191
12192 /**
12193  * @class Roo.UpdateManager.defaults
12194  * @static (not really - but it helps the doc tool)
12195  * The defaults collection enables customizing the default properties of UpdateManager
12196  */
12197    Roo.UpdateManager.defaults = {
12198        /**
12199          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12200          * @type Number
12201          */
12202          timeout : 30,
12203
12204          /**
12205          * True to process scripts by default (Defaults to false).
12206          * @type Boolean
12207          */
12208         loadScripts : false,
12209
12210         /**
12211         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12212         * @type String
12213         */
12214         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12215         /**
12216          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12217          * @type Boolean
12218          */
12219         disableCaching : false,
12220         /**
12221          * Whether to show indicatorText when loading (Defaults to true).
12222          * @type Boolean
12223          */
12224         showLoadIndicator : true,
12225         /**
12226          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12227          * @type String
12228          */
12229         indicatorText : '<div class="loading-indicator">Loading...</div>'
12230    };
12231
12232 /**
12233  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12234  *Usage:
12235  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12236  * @param {String/HTMLElement/Roo.Element} el The element to update
12237  * @param {String} url The url
12238  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12239  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12240  * @static
12241  * @deprecated
12242  * @member Roo.UpdateManager
12243  */
12244 Roo.UpdateManager.updateElement = function(el, url, params, options){
12245     var um = Roo.get(el, true).getUpdateManager();
12246     Roo.apply(um, options);
12247     um.update(url, params, options ? options.callback : null);
12248 };
12249 // alias for backwards compat
12250 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12251 /**
12252  * @class Roo.UpdateManager.BasicRenderer
12253  * Default Content renderer. Updates the elements innerHTML with the responseText.
12254  */
12255 Roo.UpdateManager.BasicRenderer = function(){};
12256
12257 Roo.UpdateManager.BasicRenderer.prototype = {
12258     /**
12259      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12260      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12261      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12262      * @param {Roo.Element} el The element being rendered
12263      * @param {Object} response The YUI Connect response object
12264      * @param {UpdateManager} updateManager The calling update manager
12265      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12266      */
12267      render : function(el, response, updateManager, callback){
12268         el.update(response.responseText, updateManager.loadScripts, callback);
12269     }
12270 };
12271 /*
12272  * Based on:
12273  * Roo JS
12274  * (c)) Alan Knowles
12275  * Licence : LGPL
12276  */
12277
12278
12279 /**
12280  * @class Roo.DomTemplate
12281  * @extends Roo.Template
12282  * An effort at a dom based template engine..
12283  *
12284  * Similar to XTemplate, except it uses dom parsing to create the template..
12285  *
12286  * Supported features:
12287  *
12288  *  Tags:
12289
12290 <pre><code>
12291       {a_variable} - output encoded.
12292       {a_variable.format:("Y-m-d")} - call a method on the variable
12293       {a_variable:raw} - unencoded output
12294       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12295       {a_variable:this.method_on_template(...)} - call a method on the template object.
12296  
12297 </code></pre>
12298  *  The tpl tag:
12299 <pre><code>
12300         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12301         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12302         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12303         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12304   
12305 </code></pre>
12306  *      
12307  */
12308 Roo.DomTemplate = function()
12309 {
12310      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12311      if (this.html) {
12312         this.compile();
12313      }
12314 };
12315
12316
12317 Roo.extend(Roo.DomTemplate, Roo.Template, {
12318     /**
12319      * id counter for sub templates.
12320      */
12321     id : 0,
12322     /**
12323      * flag to indicate if dom parser is inside a pre,
12324      * it will strip whitespace if not.
12325      */
12326     inPre : false,
12327     
12328     /**
12329      * The various sub templates
12330      */
12331     tpls : false,
12332     
12333     
12334     
12335     /**
12336      *
12337      * basic tag replacing syntax
12338      * WORD:WORD()
12339      *
12340      * // you can fake an object call by doing this
12341      *  x.t:(test,tesT) 
12342      * 
12343      */
12344     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12345     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12346     
12347     iterChild : function (node, method) {
12348         
12349         var oldPre = this.inPre;
12350         if (node.tagName == 'PRE') {
12351             this.inPre = true;
12352         }
12353         for( var i = 0; i < node.childNodes.length; i++) {
12354             method.call(this, node.childNodes[i]);
12355         }
12356         this.inPre = oldPre;
12357     },
12358     
12359     
12360     
12361     /**
12362      * compile the template
12363      *
12364      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12365      *
12366      */
12367     compile: function()
12368     {
12369         var s = this.html;
12370         
12371         // covert the html into DOM...
12372         var doc = false;
12373         var div =false;
12374         try {
12375             doc = document.implementation.createHTMLDocument("");
12376             doc.documentElement.innerHTML =   this.html  ;
12377             div = doc.documentElement;
12378         } catch (e) {
12379             // old IE... - nasty -- it causes all sorts of issues.. with
12380             // images getting pulled from server..
12381             div = document.createElement('div');
12382             div.innerHTML = this.html;
12383         }
12384         //doc.documentElement.innerHTML = htmlBody
12385          
12386         
12387         
12388         this.tpls = [];
12389         var _t = this;
12390         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12391         
12392         var tpls = this.tpls;
12393         
12394         // create a top level template from the snippet..
12395         
12396         //Roo.log(div.innerHTML);
12397         
12398         var tpl = {
12399             uid : 'master',
12400             id : this.id++,
12401             attr : false,
12402             value : false,
12403             body : div.innerHTML,
12404             
12405             forCall : false,
12406             execCall : false,
12407             dom : div,
12408             isTop : true
12409             
12410         };
12411         tpls.unshift(tpl);
12412         
12413         
12414         // compile them...
12415         this.tpls = [];
12416         Roo.each(tpls, function(tp){
12417             this.compileTpl(tp);
12418             this.tpls[tp.id] = tp;
12419         }, this);
12420         
12421         this.master = tpls[0];
12422         return this;
12423         
12424         
12425     },
12426     
12427     compileNode : function(node, istop) {
12428         // test for
12429         //Roo.log(node);
12430         
12431         
12432         // skip anything not a tag..
12433         if (node.nodeType != 1) {
12434             if (node.nodeType == 3 && !this.inPre) {
12435                 // reduce white space..
12436                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12437                 
12438             }
12439             return;
12440         }
12441         
12442         var tpl = {
12443             uid : false,
12444             id : false,
12445             attr : false,
12446             value : false,
12447             body : '',
12448             
12449             forCall : false,
12450             execCall : false,
12451             dom : false,
12452             isTop : istop
12453             
12454             
12455         };
12456         
12457         
12458         switch(true) {
12459             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12460             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12461             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12462             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12463             // no default..
12464         }
12465         
12466         
12467         if (!tpl.attr) {
12468             // just itterate children..
12469             this.iterChild(node,this.compileNode);
12470             return;
12471         }
12472         tpl.uid = this.id++;
12473         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12474         node.removeAttribute('roo-'+ tpl.attr);
12475         if (tpl.attr != 'name') {
12476             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12477             node.parentNode.replaceChild(placeholder,  node);
12478         } else {
12479             
12480             var placeholder =  document.createElement('span');
12481             placeholder.className = 'roo-tpl-' + tpl.value;
12482             node.parentNode.replaceChild(placeholder,  node);
12483         }
12484         
12485         // parent now sees '{domtplXXXX}
12486         this.iterChild(node,this.compileNode);
12487         
12488         // we should now have node body...
12489         var div = document.createElement('div');
12490         div.appendChild(node);
12491         tpl.dom = node;
12492         // this has the unfortunate side effect of converting tagged attributes
12493         // eg. href="{...}" into %7C...%7D
12494         // this has been fixed by searching for those combo's although it's a bit hacky..
12495         
12496         
12497         tpl.body = div.innerHTML;
12498         
12499         
12500          
12501         tpl.id = tpl.uid;
12502         switch(tpl.attr) {
12503             case 'for' :
12504                 switch (tpl.value) {
12505                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12506                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12507                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12508                 }
12509                 break;
12510             
12511             case 'exec':
12512                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12513                 break;
12514             
12515             case 'if':     
12516                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12517                 break;
12518             
12519             case 'name':
12520                 tpl.id  = tpl.value; // replace non characters???
12521                 break;
12522             
12523         }
12524         
12525         
12526         this.tpls.push(tpl);
12527         
12528         
12529         
12530     },
12531     
12532     
12533     
12534     
12535     /**
12536      * Compile a segment of the template into a 'sub-template'
12537      *
12538      * 
12539      * 
12540      *
12541      */
12542     compileTpl : function(tpl)
12543     {
12544         var fm = Roo.util.Format;
12545         var useF = this.disableFormats !== true;
12546         
12547         var sep = Roo.isGecko ? "+\n" : ",\n";
12548         
12549         var undef = function(str) {
12550             Roo.debug && Roo.log("Property not found :"  + str);
12551             return '';
12552         };
12553           
12554         //Roo.log(tpl.body);
12555         
12556         
12557         
12558         var fn = function(m, lbrace, name, format, args)
12559         {
12560             //Roo.log("ARGS");
12561             //Roo.log(arguments);
12562             args = args ? args.replace(/\\'/g,"'") : args;
12563             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12564             if (typeof(format) == 'undefined') {
12565                 format =  'htmlEncode'; 
12566             }
12567             if (format == 'raw' ) {
12568                 format = false;
12569             }
12570             
12571             if(name.substr(0, 6) == 'domtpl'){
12572                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12573             }
12574             
12575             // build an array of options to determine if value is undefined..
12576             
12577             // basically get 'xxxx.yyyy' then do
12578             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12579             //    (function () { Roo.log("Property not found"); return ''; })() :
12580             //    ......
12581             
12582             var udef_ar = [];
12583             var lookfor = '';
12584             Roo.each(name.split('.'), function(st) {
12585                 lookfor += (lookfor.length ? '.': '') + st;
12586                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12587             });
12588             
12589             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12590             
12591             
12592             if(format && useF){
12593                 
12594                 args = args ? ',' + args : "";
12595                  
12596                 if(format.substr(0, 5) != "this."){
12597                     format = "fm." + format + '(';
12598                 }else{
12599                     format = 'this.call("'+ format.substr(5) + '", ';
12600                     args = ", values";
12601                 }
12602                 
12603                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12604             }
12605              
12606             if (args && args.length) {
12607                 // called with xxyx.yuu:(test,test)
12608                 // change to ()
12609                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12610             }
12611             // raw.. - :raw modifier..
12612             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12613             
12614         };
12615         var body;
12616         // branched to use + in gecko and [].join() in others
12617         if(Roo.isGecko){
12618             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12619                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12620                     "';};};";
12621         }else{
12622             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12623             body.push(tpl.body.replace(/(\r\n|\n)/g,
12624                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12625             body.push("'].join('');};};");
12626             body = body.join('');
12627         }
12628         
12629         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12630        
12631         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12632         eval(body);
12633         
12634         return this;
12635     },
12636      
12637     /**
12638      * same as applyTemplate, except it's done to one of the subTemplates
12639      * when using named templates, you can do:
12640      *
12641      * var str = pl.applySubTemplate('your-name', values);
12642      *
12643      * 
12644      * @param {Number} id of the template
12645      * @param {Object} values to apply to template
12646      * @param {Object} parent (normaly the instance of this object)
12647      */
12648     applySubTemplate : function(id, values, parent)
12649     {
12650         
12651         
12652         var t = this.tpls[id];
12653         
12654         
12655         try { 
12656             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12657                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12658                 return '';
12659             }
12660         } catch(e) {
12661             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12662             Roo.log(values);
12663           
12664             return '';
12665         }
12666         try { 
12667             
12668             if(t.execCall && t.execCall.call(this, values, parent)){
12669                 return '';
12670             }
12671         } catch(e) {
12672             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12673             Roo.log(values);
12674             return '';
12675         }
12676         
12677         try {
12678             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12679             parent = t.target ? values : parent;
12680             if(t.forCall && vs instanceof Array){
12681                 var buf = [];
12682                 for(var i = 0, len = vs.length; i < len; i++){
12683                     try {
12684                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12685                     } catch (e) {
12686                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12687                         Roo.log(e.body);
12688                         //Roo.log(t.compiled);
12689                         Roo.log(vs[i]);
12690                     }   
12691                 }
12692                 return buf.join('');
12693             }
12694         } catch (e) {
12695             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12696             Roo.log(values);
12697             return '';
12698         }
12699         try {
12700             return t.compiled.call(this, vs, parent);
12701         } catch (e) {
12702             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12703             Roo.log(e.body);
12704             //Roo.log(t.compiled);
12705             Roo.log(values);
12706             return '';
12707         }
12708     },
12709
12710    
12711
12712     applyTemplate : function(values){
12713         return this.master.compiled.call(this, values, {});
12714         //var s = this.subs;
12715     },
12716
12717     apply : function(){
12718         return this.applyTemplate.apply(this, arguments);
12719     }
12720
12721  });
12722
12723 Roo.DomTemplate.from = function(el){
12724     el = Roo.getDom(el);
12725     return new Roo.Domtemplate(el.value || el.innerHTML);
12726 };/*
12727  * Based on:
12728  * Ext JS Library 1.1.1
12729  * Copyright(c) 2006-2007, Ext JS, LLC.
12730  *
12731  * Originally Released Under LGPL - original licence link has changed is not relivant.
12732  *
12733  * Fork - LGPL
12734  * <script type="text/javascript">
12735  */
12736
12737 /**
12738  * @class Roo.util.DelayedTask
12739  * Provides a convenient method of performing setTimeout where a new
12740  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12741  * You can use this class to buffer
12742  * the keypress events for a certain number of milliseconds, and perform only if they stop
12743  * for that amount of time.
12744  * @constructor The parameters to this constructor serve as defaults and are not required.
12745  * @param {Function} fn (optional) The default function to timeout
12746  * @param {Object} scope (optional) The default scope of that timeout
12747  * @param {Array} args (optional) The default Array of arguments
12748  */
12749 Roo.util.DelayedTask = function(fn, scope, args){
12750     var id = null, d, t;
12751
12752     var call = function(){
12753         var now = new Date().getTime();
12754         if(now - t >= d){
12755             clearInterval(id);
12756             id = null;
12757             fn.apply(scope, args || []);
12758         }
12759     };
12760     /**
12761      * Cancels any pending timeout and queues a new one
12762      * @param {Number} delay The milliseconds to delay
12763      * @param {Function} newFn (optional) Overrides function passed to constructor
12764      * @param {Object} newScope (optional) Overrides scope passed to constructor
12765      * @param {Array} newArgs (optional) Overrides args passed to constructor
12766      */
12767     this.delay = function(delay, newFn, newScope, newArgs){
12768         if(id && delay != d){
12769             this.cancel();
12770         }
12771         d = delay;
12772         t = new Date().getTime();
12773         fn = newFn || fn;
12774         scope = newScope || scope;
12775         args = newArgs || args;
12776         if(!id){
12777             id = setInterval(call, d);
12778         }
12779     };
12780
12781     /**
12782      * Cancel the last queued timeout
12783      */
12784     this.cancel = function(){
12785         if(id){
12786             clearInterval(id);
12787             id = null;
12788         }
12789     };
12790 };/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800  
12801  
12802 Roo.util.TaskRunner = function(interval){
12803     interval = interval || 10;
12804     var tasks = [], removeQueue = [];
12805     var id = 0;
12806     var running = false;
12807
12808     var stopThread = function(){
12809         running = false;
12810         clearInterval(id);
12811         id = 0;
12812     };
12813
12814     var startThread = function(){
12815         if(!running){
12816             running = true;
12817             id = setInterval(runTasks, interval);
12818         }
12819     };
12820
12821     var removeTask = function(task){
12822         removeQueue.push(task);
12823         if(task.onStop){
12824             task.onStop();
12825         }
12826     };
12827
12828     var runTasks = function(){
12829         if(removeQueue.length > 0){
12830             for(var i = 0, len = removeQueue.length; i < len; i++){
12831                 tasks.remove(removeQueue[i]);
12832             }
12833             removeQueue = [];
12834             if(tasks.length < 1){
12835                 stopThread();
12836                 return;
12837             }
12838         }
12839         var now = new Date().getTime();
12840         for(var i = 0, len = tasks.length; i < len; ++i){
12841             var t = tasks[i];
12842             var itime = now - t.taskRunTime;
12843             if(t.interval <= itime){
12844                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12845                 t.taskRunTime = now;
12846                 if(rt === false || t.taskRunCount === t.repeat){
12847                     removeTask(t);
12848                     return;
12849                 }
12850             }
12851             if(t.duration && t.duration <= (now - t.taskStartTime)){
12852                 removeTask(t);
12853             }
12854         }
12855     };
12856
12857     /**
12858      * Queues a new task.
12859      * @param {Object} task
12860      */
12861     this.start = function(task){
12862         tasks.push(task);
12863         task.taskStartTime = new Date().getTime();
12864         task.taskRunTime = 0;
12865         task.taskRunCount = 0;
12866         startThread();
12867         return task;
12868     };
12869
12870     this.stop = function(task){
12871         removeTask(task);
12872         return task;
12873     };
12874
12875     this.stopAll = function(){
12876         stopThread();
12877         for(var i = 0, len = tasks.length; i < len; i++){
12878             if(tasks[i].onStop){
12879                 tasks[i].onStop();
12880             }
12881         }
12882         tasks = [];
12883         removeQueue = [];
12884     };
12885 };
12886
12887 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12888  * Based on:
12889  * Ext JS Library 1.1.1
12890  * Copyright(c) 2006-2007, Ext JS, LLC.
12891  *
12892  * Originally Released Under LGPL - original licence link has changed is not relivant.
12893  *
12894  * Fork - LGPL
12895  * <script type="text/javascript">
12896  */
12897
12898  
12899 /**
12900  * @class Roo.util.MixedCollection
12901  * @extends Roo.util.Observable
12902  * A Collection class that maintains both numeric indexes and keys and exposes events.
12903  * @constructor
12904  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12905  * collection (defaults to false)
12906  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12907  * and return the key value for that item.  This is used when available to look up the key on items that
12908  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12909  * equivalent to providing an implementation for the {@link #getKey} method.
12910  */
12911 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12912     this.items = [];
12913     this.map = {};
12914     this.keys = [];
12915     this.length = 0;
12916     this.addEvents({
12917         /**
12918          * @event clear
12919          * Fires when the collection is cleared.
12920          */
12921         "clear" : true,
12922         /**
12923          * @event add
12924          * Fires when an item is added to the collection.
12925          * @param {Number} index The index at which the item was added.
12926          * @param {Object} o The item added.
12927          * @param {String} key The key associated with the added item.
12928          */
12929         "add" : true,
12930         /**
12931          * @event replace
12932          * Fires when an item is replaced in the collection.
12933          * @param {String} key he key associated with the new added.
12934          * @param {Object} old The item being replaced.
12935          * @param {Object} new The new item.
12936          */
12937         "replace" : true,
12938         /**
12939          * @event remove
12940          * Fires when an item is removed from the collection.
12941          * @param {Object} o The item being removed.
12942          * @param {String} key (optional) The key associated with the removed item.
12943          */
12944         "remove" : true,
12945         "sort" : true
12946     });
12947     this.allowFunctions = allowFunctions === true;
12948     if(keyFn){
12949         this.getKey = keyFn;
12950     }
12951     Roo.util.MixedCollection.superclass.constructor.call(this);
12952 };
12953
12954 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12955     allowFunctions : false,
12956     
12957 /**
12958  * Adds an item to the collection.
12959  * @param {String} key The key to associate with the item
12960  * @param {Object} o The item to add.
12961  * @return {Object} The item added.
12962  */
12963     add : function(key, o){
12964         if(arguments.length == 1){
12965             o = arguments[0];
12966             key = this.getKey(o);
12967         }
12968         if(typeof key == "undefined" || key === null){
12969             this.length++;
12970             this.items.push(o);
12971             this.keys.push(null);
12972         }else{
12973             var old = this.map[key];
12974             if(old){
12975                 return this.replace(key, o);
12976             }
12977             this.length++;
12978             this.items.push(o);
12979             this.map[key] = o;
12980             this.keys.push(key);
12981         }
12982         this.fireEvent("add", this.length-1, o, key);
12983         return o;
12984     },
12985        
12986 /**
12987   * MixedCollection has a generic way to fetch keys if you implement getKey.
12988 <pre><code>
12989 // normal way
12990 var mc = new Roo.util.MixedCollection();
12991 mc.add(someEl.dom.id, someEl);
12992 mc.add(otherEl.dom.id, otherEl);
12993 //and so on
12994
12995 // using getKey
12996 var mc = new Roo.util.MixedCollection();
12997 mc.getKey = function(el){
12998    return el.dom.id;
12999 };
13000 mc.add(someEl);
13001 mc.add(otherEl);
13002
13003 // or via the constructor
13004 var mc = new Roo.util.MixedCollection(false, function(el){
13005    return el.dom.id;
13006 });
13007 mc.add(someEl);
13008 mc.add(otherEl);
13009 </code></pre>
13010  * @param o {Object} The item for which to find the key.
13011  * @return {Object} The key for the passed item.
13012  */
13013     getKey : function(o){
13014          return o.id; 
13015     },
13016    
13017 /**
13018  * Replaces an item in the collection.
13019  * @param {String} key The key associated with the item to replace, or the item to replace.
13020  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13021  * @return {Object}  The new item.
13022  */
13023     replace : function(key, o){
13024         if(arguments.length == 1){
13025             o = arguments[0];
13026             key = this.getKey(o);
13027         }
13028         var old = this.item(key);
13029         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13030              return this.add(key, o);
13031         }
13032         var index = this.indexOfKey(key);
13033         this.items[index] = o;
13034         this.map[key] = o;
13035         this.fireEvent("replace", key, old, o);
13036         return o;
13037     },
13038    
13039 /**
13040  * Adds all elements of an Array or an Object to the collection.
13041  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13042  * an Array of values, each of which are added to the collection.
13043  */
13044     addAll : function(objs){
13045         if(arguments.length > 1 || objs instanceof Array){
13046             var args = arguments.length > 1 ? arguments : objs;
13047             for(var i = 0, len = args.length; i < len; i++){
13048                 this.add(args[i]);
13049             }
13050         }else{
13051             for(var key in objs){
13052                 if(this.allowFunctions || typeof objs[key] != "function"){
13053                     this.add(key, objs[key]);
13054                 }
13055             }
13056         }
13057     },
13058    
13059 /**
13060  * Executes the specified function once for every item in the collection, passing each
13061  * item as the first and only parameter. returning false from the function will stop the iteration.
13062  * @param {Function} fn The function to execute for each item.
13063  * @param {Object} scope (optional) The scope in which to execute the function.
13064  */
13065     each : function(fn, scope){
13066         var items = [].concat(this.items); // each safe for removal
13067         for(var i = 0, len = items.length; i < len; i++){
13068             if(fn.call(scope || items[i], items[i], i, len) === false){
13069                 break;
13070             }
13071         }
13072     },
13073    
13074 /**
13075  * Executes the specified function once for every key in the collection, passing each
13076  * key, and its associated item as the first two parameters.
13077  * @param {Function} fn The function to execute for each item.
13078  * @param {Object} scope (optional) The scope in which to execute the function.
13079  */
13080     eachKey : function(fn, scope){
13081         for(var i = 0, len = this.keys.length; i < len; i++){
13082             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13083         }
13084     },
13085    
13086 /**
13087  * Returns the first item in the collection which elicits a true return value from the
13088  * passed selection function.
13089  * @param {Function} fn The selection function to execute for each item.
13090  * @param {Object} scope (optional) The scope in which to execute the function.
13091  * @return {Object} The first item in the collection which returned true from the selection function.
13092  */
13093     find : function(fn, scope){
13094         for(var i = 0, len = this.items.length; i < len; i++){
13095             if(fn.call(scope || window, this.items[i], this.keys[i])){
13096                 return this.items[i];
13097             }
13098         }
13099         return null;
13100     },
13101    
13102 /**
13103  * Inserts an item at the specified index in the collection.
13104  * @param {Number} index The index to insert the item at.
13105  * @param {String} key The key to associate with the new item, or the item itself.
13106  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13107  * @return {Object} The item inserted.
13108  */
13109     insert : function(index, key, o){
13110         if(arguments.length == 2){
13111             o = arguments[1];
13112             key = this.getKey(o);
13113         }
13114         if(index >= this.length){
13115             return this.add(key, o);
13116         }
13117         this.length++;
13118         this.items.splice(index, 0, o);
13119         if(typeof key != "undefined" && key != null){
13120             this.map[key] = o;
13121         }
13122         this.keys.splice(index, 0, key);
13123         this.fireEvent("add", index, o, key);
13124         return o;
13125     },
13126    
13127 /**
13128  * Removed an item from the collection.
13129  * @param {Object} o The item to remove.
13130  * @return {Object} The item removed.
13131  */
13132     remove : function(o){
13133         return this.removeAt(this.indexOf(o));
13134     },
13135    
13136 /**
13137  * Remove an item from a specified index in the collection.
13138  * @param {Number} index The index within the collection of the item to remove.
13139  */
13140     removeAt : function(index){
13141         if(index < this.length && index >= 0){
13142             this.length--;
13143             var o = this.items[index];
13144             this.items.splice(index, 1);
13145             var key = this.keys[index];
13146             if(typeof key != "undefined"){
13147                 delete this.map[key];
13148             }
13149             this.keys.splice(index, 1);
13150             this.fireEvent("remove", o, key);
13151         }
13152     },
13153    
13154 /**
13155  * Removed an item associated with the passed key fom the collection.
13156  * @param {String} key The key of the item to remove.
13157  */
13158     removeKey : function(key){
13159         return this.removeAt(this.indexOfKey(key));
13160     },
13161    
13162 /**
13163  * Returns the number of items in the collection.
13164  * @return {Number} the number of items in the collection.
13165  */
13166     getCount : function(){
13167         return this.length; 
13168     },
13169    
13170 /**
13171  * Returns index within the collection of the passed Object.
13172  * @param {Object} o The item to find the index of.
13173  * @return {Number} index of the item.
13174  */
13175     indexOf : function(o){
13176         if(!this.items.indexOf){
13177             for(var i = 0, len = this.items.length; i < len; i++){
13178                 if(this.items[i] == o) {
13179                     return i;
13180                 }
13181             }
13182             return -1;
13183         }else{
13184             return this.items.indexOf(o);
13185         }
13186     },
13187    
13188 /**
13189  * Returns index within the collection of the passed key.
13190  * @param {String} key The key to find the index of.
13191  * @return {Number} index of the key.
13192  */
13193     indexOfKey : function(key){
13194         if(!this.keys.indexOf){
13195             for(var i = 0, len = this.keys.length; i < len; i++){
13196                 if(this.keys[i] == key) {
13197                     return i;
13198                 }
13199             }
13200             return -1;
13201         }else{
13202             return this.keys.indexOf(key);
13203         }
13204     },
13205    
13206 /**
13207  * Returns the item associated with the passed key OR index. Key has priority over index.
13208  * @param {String/Number} key The key or index of the item.
13209  * @return {Object} The item associated with the passed key.
13210  */
13211     item : function(key){
13212         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13213         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13214     },
13215     
13216 /**
13217  * Returns the item at the specified index.
13218  * @param {Number} index The index of the item.
13219  * @return {Object}
13220  */
13221     itemAt : function(index){
13222         return this.items[index];
13223     },
13224     
13225 /**
13226  * Returns the item associated with the passed key.
13227  * @param {String/Number} key The key of the item.
13228  * @return {Object} The item associated with the passed key.
13229  */
13230     key : function(key){
13231         return this.map[key];
13232     },
13233    
13234 /**
13235  * Returns true if the collection contains the passed Object as an item.
13236  * @param {Object} o  The Object to look for in the collection.
13237  * @return {Boolean} True if the collection contains the Object as an item.
13238  */
13239     contains : function(o){
13240         return this.indexOf(o) != -1;
13241     },
13242    
13243 /**
13244  * Returns true if the collection contains the passed Object as a key.
13245  * @param {String} key The key to look for in the collection.
13246  * @return {Boolean} True if the collection contains the Object as a key.
13247  */
13248     containsKey : function(key){
13249         return typeof this.map[key] != "undefined";
13250     },
13251    
13252 /**
13253  * Removes all items from the collection.
13254  */
13255     clear : function(){
13256         this.length = 0;
13257         this.items = [];
13258         this.keys = [];
13259         this.map = {};
13260         this.fireEvent("clear");
13261     },
13262    
13263 /**
13264  * Returns the first item in the collection.
13265  * @return {Object} the first item in the collection..
13266  */
13267     first : function(){
13268         return this.items[0]; 
13269     },
13270    
13271 /**
13272  * Returns the last item in the collection.
13273  * @return {Object} the last item in the collection..
13274  */
13275     last : function(){
13276         return this.items[this.length-1];   
13277     },
13278     
13279     _sort : function(property, dir, fn){
13280         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13281         fn = fn || function(a, b){
13282             return a-b;
13283         };
13284         var c = [], k = this.keys, items = this.items;
13285         for(var i = 0, len = items.length; i < len; i++){
13286             c[c.length] = {key: k[i], value: items[i], index: i};
13287         }
13288         c.sort(function(a, b){
13289             var v = fn(a[property], b[property]) * dsc;
13290             if(v == 0){
13291                 v = (a.index < b.index ? -1 : 1);
13292             }
13293             return v;
13294         });
13295         for(var i = 0, len = c.length; i < len; i++){
13296             items[i] = c[i].value;
13297             k[i] = c[i].key;
13298         }
13299         this.fireEvent("sort", this);
13300     },
13301     
13302     /**
13303      * Sorts this collection with the passed comparison function
13304      * @param {String} direction (optional) "ASC" or "DESC"
13305      * @param {Function} fn (optional) comparison function
13306      */
13307     sort : function(dir, fn){
13308         this._sort("value", dir, fn);
13309     },
13310     
13311     /**
13312      * Sorts this collection by keys
13313      * @param {String} direction (optional) "ASC" or "DESC"
13314      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13315      */
13316     keySort : function(dir, fn){
13317         this._sort("key", dir, fn || function(a, b){
13318             return String(a).toUpperCase()-String(b).toUpperCase();
13319         });
13320     },
13321     
13322     /**
13323      * Returns a range of items in this collection
13324      * @param {Number} startIndex (optional) defaults to 0
13325      * @param {Number} endIndex (optional) default to the last item
13326      * @return {Array} An array of items
13327      */
13328     getRange : function(start, end){
13329         var items = this.items;
13330         if(items.length < 1){
13331             return [];
13332         }
13333         start = start || 0;
13334         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13335         var r = [];
13336         if(start <= end){
13337             for(var i = start; i <= end; i++) {
13338                     r[r.length] = items[i];
13339             }
13340         }else{
13341             for(var i = start; i >= end; i--) {
13342                     r[r.length] = items[i];
13343             }
13344         }
13345         return r;
13346     },
13347         
13348     /**
13349      * Filter the <i>objects</i> in this collection by a specific property. 
13350      * Returns a new collection that has been filtered.
13351      * @param {String} property A property on your objects
13352      * @param {String/RegExp} value Either string that the property values 
13353      * should start with or a RegExp to test against the property
13354      * @return {MixedCollection} The new filtered collection
13355      */
13356     filter : function(property, value){
13357         if(!value.exec){ // not a regex
13358             value = String(value);
13359             if(value.length == 0){
13360                 return this.clone();
13361             }
13362             value = new RegExp("^" + Roo.escapeRe(value), "i");
13363         }
13364         return this.filterBy(function(o){
13365             return o && value.test(o[property]);
13366         });
13367         },
13368     
13369     /**
13370      * Filter by a function. * Returns a new collection that has been filtered.
13371      * The passed function will be called with each 
13372      * object in the collection. If the function returns true, the value is included 
13373      * otherwise it is filtered.
13374      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13375      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13376      * @return {MixedCollection} The new filtered collection
13377      */
13378     filterBy : function(fn, scope){
13379         var r = new Roo.util.MixedCollection();
13380         r.getKey = this.getKey;
13381         var k = this.keys, it = this.items;
13382         for(var i = 0, len = it.length; i < len; i++){
13383             if(fn.call(scope||this, it[i], k[i])){
13384                                 r.add(k[i], it[i]);
13385                         }
13386         }
13387         return r;
13388     },
13389     
13390     /**
13391      * Creates a duplicate of this collection
13392      * @return {MixedCollection}
13393      */
13394     clone : function(){
13395         var r = new Roo.util.MixedCollection();
13396         var k = this.keys, it = this.items;
13397         for(var i = 0, len = it.length; i < len; i++){
13398             r.add(k[i], it[i]);
13399         }
13400         r.getKey = this.getKey;
13401         return r;
13402     }
13403 });
13404 /**
13405  * Returns the item associated with the passed key or index.
13406  * @method
13407  * @param {String/Number} key The key or index of the item.
13408  * @return {Object} The item associated with the passed key.
13409  */
13410 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13411  * Based on:
13412  * Ext JS Library 1.1.1
13413  * Copyright(c) 2006-2007, Ext JS, LLC.
13414  *
13415  * Originally Released Under LGPL - original licence link has changed is not relivant.
13416  *
13417  * Fork - LGPL
13418  * <script type="text/javascript">
13419  */
13420 /**
13421  * @class Roo.util.JSON
13422  * Modified version of Douglas Crockford"s json.js that doesn"t
13423  * mess with the Object prototype 
13424  * http://www.json.org/js.html
13425  * @singleton
13426  */
13427 Roo.util.JSON = new (function(){
13428     var useHasOwn = {}.hasOwnProperty ? true : false;
13429     
13430     // crashes Safari in some instances
13431     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13432     
13433     var pad = function(n) {
13434         return n < 10 ? "0" + n : n;
13435     };
13436     
13437     var m = {
13438         "\b": '\\b',
13439         "\t": '\\t',
13440         "\n": '\\n',
13441         "\f": '\\f',
13442         "\r": '\\r',
13443         '"' : '\\"',
13444         "\\": '\\\\'
13445     };
13446
13447     var encodeString = function(s){
13448         if (/["\\\x00-\x1f]/.test(s)) {
13449             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13450                 var c = m[b];
13451                 if(c){
13452                     return c;
13453                 }
13454                 c = b.charCodeAt();
13455                 return "\\u00" +
13456                     Math.floor(c / 16).toString(16) +
13457                     (c % 16).toString(16);
13458             }) + '"';
13459         }
13460         return '"' + s + '"';
13461     };
13462     
13463     var encodeArray = function(o){
13464         var a = ["["], b, i, l = o.length, v;
13465             for (i = 0; i < l; i += 1) {
13466                 v = o[i];
13467                 switch (typeof v) {
13468                     case "undefined":
13469                     case "function":
13470                     case "unknown":
13471                         break;
13472                     default:
13473                         if (b) {
13474                             a.push(',');
13475                         }
13476                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13477                         b = true;
13478                 }
13479             }
13480             a.push("]");
13481             return a.join("");
13482     };
13483     
13484     var encodeDate = function(o){
13485         return '"' + o.getFullYear() + "-" +
13486                 pad(o.getMonth() + 1) + "-" +
13487                 pad(o.getDate()) + "T" +
13488                 pad(o.getHours()) + ":" +
13489                 pad(o.getMinutes()) + ":" +
13490                 pad(o.getSeconds()) + '"';
13491     };
13492     
13493     /**
13494      * Encodes an Object, Array or other value
13495      * @param {Mixed} o The variable to encode
13496      * @return {String} The JSON string
13497      */
13498     this.encode = function(o)
13499     {
13500         // should this be extended to fully wrap stringify..
13501         
13502         if(typeof o == "undefined" || o === null){
13503             return "null";
13504         }else if(o instanceof Array){
13505             return encodeArray(o);
13506         }else if(o instanceof Date){
13507             return encodeDate(o);
13508         }else if(typeof o == "string"){
13509             return encodeString(o);
13510         }else if(typeof o == "number"){
13511             return isFinite(o) ? String(o) : "null";
13512         }else if(typeof o == "boolean"){
13513             return String(o);
13514         }else {
13515             var a = ["{"], b, i, v;
13516             for (i in o) {
13517                 if(!useHasOwn || o.hasOwnProperty(i)) {
13518                     v = o[i];
13519                     switch (typeof v) {
13520                     case "undefined":
13521                     case "function":
13522                     case "unknown":
13523                         break;
13524                     default:
13525                         if(b){
13526                             a.push(',');
13527                         }
13528                         a.push(this.encode(i), ":",
13529                                 v === null ? "null" : this.encode(v));
13530                         b = true;
13531                     }
13532                 }
13533             }
13534             a.push("}");
13535             return a.join("");
13536         }
13537     };
13538     
13539     /**
13540      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13541      * @param {String} json The JSON string
13542      * @return {Object} The resulting object
13543      */
13544     this.decode = function(json){
13545         
13546         return  /** eval:var:json */ eval("(" + json + ')');
13547     };
13548 })();
13549 /** 
13550  * Shorthand for {@link Roo.util.JSON#encode}
13551  * @member Roo encode 
13552  * @method */
13553 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13554 /** 
13555  * Shorthand for {@link Roo.util.JSON#decode}
13556  * @member Roo decode 
13557  * @method */
13558 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13559 /*
13560  * Based on:
13561  * Ext JS Library 1.1.1
13562  * Copyright(c) 2006-2007, Ext JS, LLC.
13563  *
13564  * Originally Released Under LGPL - original licence link has changed is not relivant.
13565  *
13566  * Fork - LGPL
13567  * <script type="text/javascript">
13568  */
13569  
13570 /**
13571  * @class Roo.util.Format
13572  * Reusable data formatting functions
13573  * @singleton
13574  */
13575 Roo.util.Format = function(){
13576     var trimRe = /^\s+|\s+$/g;
13577     return {
13578         /**
13579          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13580          * @param {String} value The string to truncate
13581          * @param {Number} length The maximum length to allow before truncating
13582          * @return {String} The converted text
13583          */
13584         ellipsis : function(value, len){
13585             if(value && value.length > len){
13586                 return value.substr(0, len-3)+"...";
13587             }
13588             return value;
13589         },
13590
13591         /**
13592          * Checks a reference and converts it to empty string if it is undefined
13593          * @param {Mixed} value Reference to check
13594          * @return {Mixed} Empty string if converted, otherwise the original value
13595          */
13596         undef : function(value){
13597             return typeof value != "undefined" ? value : "";
13598         },
13599
13600         /**
13601          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13602          * @param {String} value The string to encode
13603          * @return {String} The encoded text
13604          */
13605         htmlEncode : function(value){
13606             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13607         },
13608
13609         /**
13610          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13611          * @param {String} value The string to decode
13612          * @return {String} The decoded text
13613          */
13614         htmlDecode : function(value){
13615             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13616         },
13617
13618         /**
13619          * Trims any whitespace from either side of a string
13620          * @param {String} value The text to trim
13621          * @return {String} The trimmed text
13622          */
13623         trim : function(value){
13624             return String(value).replace(trimRe, "");
13625         },
13626
13627         /**
13628          * Returns a substring from within an original string
13629          * @param {String} value The original text
13630          * @param {Number} start The start index of the substring
13631          * @param {Number} length The length of the substring
13632          * @return {String} The substring
13633          */
13634         substr : function(value, start, length){
13635             return String(value).substr(start, length);
13636         },
13637
13638         /**
13639          * Converts a string to all lower case letters
13640          * @param {String} value The text to convert
13641          * @return {String} The converted text
13642          */
13643         lowercase : function(value){
13644             return String(value).toLowerCase();
13645         },
13646
13647         /**
13648          * Converts a string to all upper case letters
13649          * @param {String} value The text to convert
13650          * @return {String} The converted text
13651          */
13652         uppercase : function(value){
13653             return String(value).toUpperCase();
13654         },
13655
13656         /**
13657          * Converts the first character only of a string to upper case
13658          * @param {String} value The text to convert
13659          * @return {String} The converted text
13660          */
13661         capitalize : function(value){
13662             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13663         },
13664
13665         // private
13666         call : function(value, fn){
13667             if(arguments.length > 2){
13668                 var args = Array.prototype.slice.call(arguments, 2);
13669                 args.unshift(value);
13670                  
13671                 return /** eval:var:value */  eval(fn).apply(window, args);
13672             }else{
13673                 /** eval:var:value */
13674                 return /** eval:var:value */ eval(fn).call(window, value);
13675             }
13676         },
13677
13678        
13679         /**
13680          * safer version of Math.toFixed..??/
13681          * @param {Number/String} value The numeric value to format
13682          * @param {Number/String} value Decimal places 
13683          * @return {String} The formatted currency string
13684          */
13685         toFixed : function(v, n)
13686         {
13687             // why not use to fixed - precision is buggered???
13688             if (!n) {
13689                 return Math.round(v-0);
13690             }
13691             var fact = Math.pow(10,n+1);
13692             v = (Math.round((v-0)*fact))/fact;
13693             var z = (''+fact).substring(2);
13694             if (v == Math.floor(v)) {
13695                 return Math.floor(v) + '.' + z;
13696             }
13697             
13698             // now just padd decimals..
13699             var ps = String(v).split('.');
13700             var fd = (ps[1] + z);
13701             var r = fd.substring(0,n); 
13702             var rm = fd.substring(n); 
13703             if (rm < 5) {
13704                 return ps[0] + '.' + r;
13705             }
13706             r*=1; // turn it into a number;
13707             r++;
13708             if (String(r).length != n) {
13709                 ps[0]*=1;
13710                 ps[0]++;
13711                 r = String(r).substring(1); // chop the end off.
13712             }
13713             
13714             return ps[0] + '.' + r;
13715              
13716         },
13717         
13718         /**
13719          * Format a number as US currency
13720          * @param {Number/String} value The numeric value to format
13721          * @return {String} The formatted currency string
13722          */
13723         usMoney : function(v){
13724             return '$' + Roo.util.Format.number(v);
13725         },
13726         
13727         /**
13728          * Format a number
13729          * eventually this should probably emulate php's number_format
13730          * @param {Number/String} value The numeric value to format
13731          * @param {Number} decimals number of decimal places
13732          * @return {String} The formatted currency string
13733          */
13734         number : function(v,decimals)
13735         {
13736             // multiply and round.
13737             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13738             var mul = Math.pow(10, decimals);
13739             var zero = String(mul).substring(1);
13740             v = (Math.round((v-0)*mul))/mul;
13741             
13742             // if it's '0' number.. then
13743             
13744             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13745             v = String(v);
13746             var ps = v.split('.');
13747             var whole = ps[0];
13748             
13749             
13750             var r = /(\d+)(\d{3})/;
13751             // add comma's
13752             while (r.test(whole)) {
13753                 whole = whole.replace(r, '$1' + ',' + '$2');
13754             }
13755             
13756             
13757             var sub = ps[1] ?
13758                     // has decimals..
13759                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13760                     // does not have decimals
13761                     (decimals ? ('.' + zero) : '');
13762             
13763             
13764             return whole + sub ;
13765         },
13766         
13767         /**
13768          * Parse a value into a formatted date using the specified format pattern.
13769          * @param {Mixed} value The value to format
13770          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13771          * @return {String} The formatted date string
13772          */
13773         date : function(v, format){
13774             if(!v){
13775                 return "";
13776             }
13777             if(!(v instanceof Date)){
13778                 v = new Date(Date.parse(v));
13779             }
13780             return v.dateFormat(format || Roo.util.Format.defaults.date);
13781         },
13782
13783         /**
13784          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13785          * @param {String} format Any valid date format string
13786          * @return {Function} The date formatting function
13787          */
13788         dateRenderer : function(format){
13789             return function(v){
13790                 return Roo.util.Format.date(v, format);  
13791             };
13792         },
13793
13794         // private
13795         stripTagsRE : /<\/?[^>]+>/gi,
13796         
13797         /**
13798          * Strips all HTML tags
13799          * @param {Mixed} value The text from which to strip tags
13800          * @return {String} The stripped text
13801          */
13802         stripTags : function(v){
13803             return !v ? v : String(v).replace(this.stripTagsRE, "");
13804         }
13805     };
13806 }();
13807 Roo.util.Format.defaults = {
13808     date : 'd/M/Y'
13809 };/*
13810  * Based on:
13811  * Ext JS Library 1.1.1
13812  * Copyright(c) 2006-2007, Ext JS, LLC.
13813  *
13814  * Originally Released Under LGPL - original licence link has changed is not relivant.
13815  *
13816  * Fork - LGPL
13817  * <script type="text/javascript">
13818  */
13819
13820
13821  
13822
13823 /**
13824  * @class Roo.MasterTemplate
13825  * @extends Roo.Template
13826  * Provides a template that can have child templates. The syntax is:
13827 <pre><code>
13828 var t = new Roo.MasterTemplate(
13829         '&lt;select name="{name}"&gt;',
13830                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13831         '&lt;/select&gt;'
13832 );
13833 t.add('options', {value: 'foo', text: 'bar'});
13834 // or you can add multiple child elements in one shot
13835 t.addAll('options', [
13836     {value: 'foo', text: 'bar'},
13837     {value: 'foo2', text: 'bar2'},
13838     {value: 'foo3', text: 'bar3'}
13839 ]);
13840 // then append, applying the master template values
13841 t.append('my-form', {name: 'my-select'});
13842 </code></pre>
13843 * A name attribute for the child template is not required if you have only one child
13844 * template or you want to refer to them by index.
13845  */
13846 Roo.MasterTemplate = function(){
13847     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13848     this.originalHtml = this.html;
13849     var st = {};
13850     var m, re = this.subTemplateRe;
13851     re.lastIndex = 0;
13852     var subIndex = 0;
13853     while(m = re.exec(this.html)){
13854         var name = m[1], content = m[2];
13855         st[subIndex] = {
13856             name: name,
13857             index: subIndex,
13858             buffer: [],
13859             tpl : new Roo.Template(content)
13860         };
13861         if(name){
13862             st[name] = st[subIndex];
13863         }
13864         st[subIndex].tpl.compile();
13865         st[subIndex].tpl.call = this.call.createDelegate(this);
13866         subIndex++;
13867     }
13868     this.subCount = subIndex;
13869     this.subs = st;
13870 };
13871 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13872     /**
13873     * The regular expression used to match sub templates
13874     * @type RegExp
13875     * @property
13876     */
13877     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13878
13879     /**
13880      * Applies the passed values to a child template.
13881      * @param {String/Number} name (optional) The name or index of the child template
13882      * @param {Array/Object} values The values to be applied to the template
13883      * @return {MasterTemplate} this
13884      */
13885      add : function(name, values){
13886         if(arguments.length == 1){
13887             values = arguments[0];
13888             name = 0;
13889         }
13890         var s = this.subs[name];
13891         s.buffer[s.buffer.length] = s.tpl.apply(values);
13892         return this;
13893     },
13894
13895     /**
13896      * Applies all the passed values to a child template.
13897      * @param {String/Number} name (optional) The name or index of the child template
13898      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13899      * @param {Boolean} reset (optional) True to reset the template first
13900      * @return {MasterTemplate} this
13901      */
13902     fill : function(name, values, reset){
13903         var a = arguments;
13904         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13905             values = a[0];
13906             name = 0;
13907             reset = a[1];
13908         }
13909         if(reset){
13910             this.reset();
13911         }
13912         for(var i = 0, len = values.length; i < len; i++){
13913             this.add(name, values[i]);
13914         }
13915         return this;
13916     },
13917
13918     /**
13919      * Resets the template for reuse
13920      * @return {MasterTemplate} this
13921      */
13922      reset : function(){
13923         var s = this.subs;
13924         for(var i = 0; i < this.subCount; i++){
13925             s[i].buffer = [];
13926         }
13927         return this;
13928     },
13929
13930     applyTemplate : function(values){
13931         var s = this.subs;
13932         var replaceIndex = -1;
13933         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13934             return s[++replaceIndex].buffer.join("");
13935         });
13936         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13937     },
13938
13939     apply : function(){
13940         return this.applyTemplate.apply(this, arguments);
13941     },
13942
13943     compile : function(){return this;}
13944 });
13945
13946 /**
13947  * Alias for fill().
13948  * @method
13949  */
13950 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13951  /**
13952  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13953  * var tpl = Roo.MasterTemplate.from('element-id');
13954  * @param {String/HTMLElement} el
13955  * @param {Object} config
13956  * @static
13957  */
13958 Roo.MasterTemplate.from = function(el, config){
13959     el = Roo.getDom(el);
13960     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13961 };/*
13962  * Based on:
13963  * Ext JS Library 1.1.1
13964  * Copyright(c) 2006-2007, Ext JS, LLC.
13965  *
13966  * Originally Released Under LGPL - original licence link has changed is not relivant.
13967  *
13968  * Fork - LGPL
13969  * <script type="text/javascript">
13970  */
13971
13972  
13973 /**
13974  * @class Roo.util.CSS
13975  * Utility class for manipulating CSS rules
13976  * @singleton
13977  */
13978 Roo.util.CSS = function(){
13979         var rules = null;
13980         var doc = document;
13981
13982     var camelRe = /(-[a-z])/gi;
13983     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13984
13985    return {
13986    /**
13987     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13988     * tag and appended to the HEAD of the document.
13989     * @param {String|Object} cssText The text containing the css rules
13990     * @param {String} id An id to add to the stylesheet for later removal
13991     * @return {StyleSheet}
13992     */
13993     createStyleSheet : function(cssText, id){
13994         var ss;
13995         var head = doc.getElementsByTagName("head")[0];
13996         var nrules = doc.createElement("style");
13997         nrules.setAttribute("type", "text/css");
13998         if(id){
13999             nrules.setAttribute("id", id);
14000         }
14001         if (typeof(cssText) != 'string') {
14002             // support object maps..
14003             // not sure if this a good idea.. 
14004             // perhaps it should be merged with the general css handling
14005             // and handle js style props.
14006             var cssTextNew = [];
14007             for(var n in cssText) {
14008                 var citems = [];
14009                 for(var k in cssText[n]) {
14010                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14011                 }
14012                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14013                 
14014             }
14015             cssText = cssTextNew.join("\n");
14016             
14017         }
14018        
14019        
14020        if(Roo.isIE){
14021            head.appendChild(nrules);
14022            ss = nrules.styleSheet;
14023            ss.cssText = cssText;
14024        }else{
14025            try{
14026                 nrules.appendChild(doc.createTextNode(cssText));
14027            }catch(e){
14028                nrules.cssText = cssText; 
14029            }
14030            head.appendChild(nrules);
14031            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14032        }
14033        this.cacheStyleSheet(ss);
14034        return ss;
14035    },
14036
14037    /**
14038     * Removes a style or link tag by id
14039     * @param {String} id The id of the tag
14040     */
14041    removeStyleSheet : function(id){
14042        var existing = doc.getElementById(id);
14043        if(existing){
14044            existing.parentNode.removeChild(existing);
14045        }
14046    },
14047
14048    /**
14049     * Dynamically swaps an existing stylesheet reference for a new one
14050     * @param {String} id The id of an existing link tag to remove
14051     * @param {String} url The href of the new stylesheet to include
14052     */
14053    swapStyleSheet : function(id, url){
14054        this.removeStyleSheet(id);
14055        var ss = doc.createElement("link");
14056        ss.setAttribute("rel", "stylesheet");
14057        ss.setAttribute("type", "text/css");
14058        ss.setAttribute("id", id);
14059        ss.setAttribute("href", url);
14060        doc.getElementsByTagName("head")[0].appendChild(ss);
14061    },
14062    
14063    /**
14064     * Refresh the rule cache if you have dynamically added stylesheets
14065     * @return {Object} An object (hash) of rules indexed by selector
14066     */
14067    refreshCache : function(){
14068        return this.getRules(true);
14069    },
14070
14071    // private
14072    cacheStyleSheet : function(stylesheet){
14073        if(!rules){
14074            rules = {};
14075        }
14076        try{// try catch for cross domain access issue
14077            var ssRules = stylesheet.cssRules || stylesheet.rules;
14078            for(var j = ssRules.length-1; j >= 0; --j){
14079                rules[ssRules[j].selectorText] = ssRules[j];
14080            }
14081        }catch(e){}
14082    },
14083    
14084    /**
14085     * Gets all css rules for the document
14086     * @param {Boolean} refreshCache true to refresh the internal cache
14087     * @return {Object} An object (hash) of rules indexed by selector
14088     */
14089    getRules : function(refreshCache){
14090                 if(rules == null || refreshCache){
14091                         rules = {};
14092                         var ds = doc.styleSheets;
14093                         for(var i =0, len = ds.length; i < len; i++){
14094                             try{
14095                         this.cacheStyleSheet(ds[i]);
14096                     }catch(e){} 
14097                 }
14098                 }
14099                 return rules;
14100         },
14101         
14102         /**
14103     * Gets an an individual CSS rule by selector(s)
14104     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14105     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14106     * @return {CSSRule} The CSS rule or null if one is not found
14107     */
14108    getRule : function(selector, refreshCache){
14109                 var rs = this.getRules(refreshCache);
14110                 if(!(selector instanceof Array)){
14111                     return rs[selector];
14112                 }
14113                 for(var i = 0; i < selector.length; i++){
14114                         if(rs[selector[i]]){
14115                                 return rs[selector[i]];
14116                         }
14117                 }
14118                 return null;
14119         },
14120         
14121         
14122         /**
14123     * Updates a rule property
14124     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14125     * @param {String} property The css property
14126     * @param {String} value The new value for the property
14127     * @return {Boolean} true If a rule was found and updated
14128     */
14129    updateRule : function(selector, property, value){
14130                 if(!(selector instanceof Array)){
14131                         var rule = this.getRule(selector);
14132                         if(rule){
14133                                 rule.style[property.replace(camelRe, camelFn)] = value;
14134                                 return true;
14135                         }
14136                 }else{
14137                         for(var i = 0; i < selector.length; i++){
14138                                 if(this.updateRule(selector[i], property, value)){
14139                                         return true;
14140                                 }
14141                         }
14142                 }
14143                 return false;
14144         }
14145    };   
14146 }();/*
14147  * Based on:
14148  * Ext JS Library 1.1.1
14149  * Copyright(c) 2006-2007, Ext JS, LLC.
14150  *
14151  * Originally Released Under LGPL - original licence link has changed is not relivant.
14152  *
14153  * Fork - LGPL
14154  * <script type="text/javascript">
14155  */
14156
14157  
14158
14159 /**
14160  * @class Roo.util.ClickRepeater
14161  * @extends Roo.util.Observable
14162  * 
14163  * A wrapper class which can be applied to any element. Fires a "click" event while the
14164  * mouse is pressed. The interval between firings may be specified in the config but
14165  * defaults to 10 milliseconds.
14166  * 
14167  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14168  * 
14169  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14170  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14171  * Similar to an autorepeat key delay.
14172  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14173  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14174  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14175  *           "interval" and "delay" are ignored. "immediate" is honored.
14176  * @cfg {Boolean} preventDefault True to prevent the default click event
14177  * @cfg {Boolean} stopDefault True to stop the default click event
14178  * 
14179  * @history
14180  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14181  *     2007-02-02 jvs Renamed to ClickRepeater
14182  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14183  *
14184  *  @constructor
14185  * @param {String/HTMLElement/Element} el The element to listen on
14186  * @param {Object} config
14187  **/
14188 Roo.util.ClickRepeater = function(el, config)
14189 {
14190     this.el = Roo.get(el);
14191     this.el.unselectable();
14192
14193     Roo.apply(this, config);
14194
14195     this.addEvents({
14196     /**
14197      * @event mousedown
14198      * Fires when the mouse button is depressed.
14199      * @param {Roo.util.ClickRepeater} this
14200      */
14201         "mousedown" : true,
14202     /**
14203      * @event click
14204      * Fires on a specified interval during the time the element is pressed.
14205      * @param {Roo.util.ClickRepeater} this
14206      */
14207         "click" : true,
14208     /**
14209      * @event mouseup
14210      * Fires when the mouse key is released.
14211      * @param {Roo.util.ClickRepeater} this
14212      */
14213         "mouseup" : true
14214     });
14215
14216     this.el.on("mousedown", this.handleMouseDown, this);
14217     if(this.preventDefault || this.stopDefault){
14218         this.el.on("click", function(e){
14219             if(this.preventDefault){
14220                 e.preventDefault();
14221             }
14222             if(this.stopDefault){
14223                 e.stopEvent();
14224             }
14225         }, this);
14226     }
14227
14228     // allow inline handler
14229     if(this.handler){
14230         this.on("click", this.handler,  this.scope || this);
14231     }
14232
14233     Roo.util.ClickRepeater.superclass.constructor.call(this);
14234 };
14235
14236 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14237     interval : 20,
14238     delay: 250,
14239     preventDefault : true,
14240     stopDefault : false,
14241     timer : 0,
14242
14243     // private
14244     handleMouseDown : function(){
14245         clearTimeout(this.timer);
14246         this.el.blur();
14247         if(this.pressClass){
14248             this.el.addClass(this.pressClass);
14249         }
14250         this.mousedownTime = new Date();
14251
14252         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14253         this.el.on("mouseout", this.handleMouseOut, this);
14254
14255         this.fireEvent("mousedown", this);
14256         this.fireEvent("click", this);
14257         
14258         this.timer = this.click.defer(this.delay || this.interval, this);
14259     },
14260
14261     // private
14262     click : function(){
14263         this.fireEvent("click", this);
14264         this.timer = this.click.defer(this.getInterval(), this);
14265     },
14266
14267     // private
14268     getInterval: function(){
14269         if(!this.accelerate){
14270             return this.interval;
14271         }
14272         var pressTime = this.mousedownTime.getElapsed();
14273         if(pressTime < 500){
14274             return 400;
14275         }else if(pressTime < 1700){
14276             return 320;
14277         }else if(pressTime < 2600){
14278             return 250;
14279         }else if(pressTime < 3500){
14280             return 180;
14281         }else if(pressTime < 4400){
14282             return 140;
14283         }else if(pressTime < 5300){
14284             return 80;
14285         }else if(pressTime < 6200){
14286             return 50;
14287         }else{
14288             return 10;
14289         }
14290     },
14291
14292     // private
14293     handleMouseOut : function(){
14294         clearTimeout(this.timer);
14295         if(this.pressClass){
14296             this.el.removeClass(this.pressClass);
14297         }
14298         this.el.on("mouseover", this.handleMouseReturn, this);
14299     },
14300
14301     // private
14302     handleMouseReturn : function(){
14303         this.el.un("mouseover", this.handleMouseReturn);
14304         if(this.pressClass){
14305             this.el.addClass(this.pressClass);
14306         }
14307         this.click();
14308     },
14309
14310     // private
14311     handleMouseUp : function(){
14312         clearTimeout(this.timer);
14313         this.el.un("mouseover", this.handleMouseReturn);
14314         this.el.un("mouseout", this.handleMouseOut);
14315         Roo.get(document).un("mouseup", this.handleMouseUp);
14316         this.el.removeClass(this.pressClass);
14317         this.fireEvent("mouseup", this);
14318     }
14319 });/*
14320  * Based on:
14321  * Ext JS Library 1.1.1
14322  * Copyright(c) 2006-2007, Ext JS, LLC.
14323  *
14324  * Originally Released Under LGPL - original licence link has changed is not relivant.
14325  *
14326  * Fork - LGPL
14327  * <script type="text/javascript">
14328  */
14329
14330  
14331 /**
14332  * @class Roo.KeyNav
14333  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14334  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14335  * way to implement custom navigation schemes for any UI component.</p>
14336  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14337  * pageUp, pageDown, del, home, end.  Usage:</p>
14338  <pre><code>
14339 var nav = new Roo.KeyNav("my-element", {
14340     "left" : function(e){
14341         this.moveLeft(e.ctrlKey);
14342     },
14343     "right" : function(e){
14344         this.moveRight(e.ctrlKey);
14345     },
14346     "enter" : function(e){
14347         this.save();
14348     },
14349     scope : this
14350 });
14351 </code></pre>
14352  * @constructor
14353  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14354  * @param {Object} config The config
14355  */
14356 Roo.KeyNav = function(el, config){
14357     this.el = Roo.get(el);
14358     Roo.apply(this, config);
14359     if(!this.disabled){
14360         this.disabled = true;
14361         this.enable();
14362     }
14363 };
14364
14365 Roo.KeyNav.prototype = {
14366     /**
14367      * @cfg {Boolean} disabled
14368      * True to disable this KeyNav instance (defaults to false)
14369      */
14370     disabled : false,
14371     /**
14372      * @cfg {String} defaultEventAction
14373      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14374      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14375      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14376      */
14377     defaultEventAction: "stopEvent",
14378     /**
14379      * @cfg {Boolean} forceKeyDown
14380      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14381      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14382      * handle keydown instead of keypress.
14383      */
14384     forceKeyDown : false,
14385
14386     // private
14387     prepareEvent : function(e){
14388         var k = e.getKey();
14389         var h = this.keyToHandler[k];
14390         //if(h && this[h]){
14391         //    e.stopPropagation();
14392         //}
14393         if(Roo.isSafari && h && k >= 37 && k <= 40){
14394             e.stopEvent();
14395         }
14396     },
14397
14398     // private
14399     relay : function(e){
14400         var k = e.getKey();
14401         var h = this.keyToHandler[k];
14402         if(h && this[h]){
14403             if(this.doRelay(e, this[h], h) !== true){
14404                 e[this.defaultEventAction]();
14405             }
14406         }
14407     },
14408
14409     // private
14410     doRelay : function(e, h, hname){
14411         return h.call(this.scope || this, e);
14412     },
14413
14414     // possible handlers
14415     enter : false,
14416     left : false,
14417     right : false,
14418     up : false,
14419     down : false,
14420     tab : false,
14421     esc : false,
14422     pageUp : false,
14423     pageDown : false,
14424     del : false,
14425     home : false,
14426     end : false,
14427
14428     // quick lookup hash
14429     keyToHandler : {
14430         37 : "left",
14431         39 : "right",
14432         38 : "up",
14433         40 : "down",
14434         33 : "pageUp",
14435         34 : "pageDown",
14436         46 : "del",
14437         36 : "home",
14438         35 : "end",
14439         13 : "enter",
14440         27 : "esc",
14441         9  : "tab"
14442     },
14443
14444         /**
14445          * Enable this KeyNav
14446          */
14447         enable: function(){
14448                 if(this.disabled){
14449             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14450             // the EventObject will normalize Safari automatically
14451             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14452                 this.el.on("keydown", this.relay,  this);
14453             }else{
14454                 this.el.on("keydown", this.prepareEvent,  this);
14455                 this.el.on("keypress", this.relay,  this);
14456             }
14457                     this.disabled = false;
14458                 }
14459         },
14460
14461         /**
14462          * Disable this KeyNav
14463          */
14464         disable: function(){
14465                 if(!this.disabled){
14466                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14467                 this.el.un("keydown", this.relay);
14468             }else{
14469                 this.el.un("keydown", this.prepareEvent);
14470                 this.el.un("keypress", this.relay);
14471             }
14472                     this.disabled = true;
14473                 }
14474         }
14475 };/*
14476  * Based on:
14477  * Ext JS Library 1.1.1
14478  * Copyright(c) 2006-2007, Ext JS, LLC.
14479  *
14480  * Originally Released Under LGPL - original licence link has changed is not relivant.
14481  *
14482  * Fork - LGPL
14483  * <script type="text/javascript">
14484  */
14485
14486  
14487 /**
14488  * @class Roo.KeyMap
14489  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14490  * The constructor accepts the same config object as defined by {@link #addBinding}.
14491  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14492  * combination it will call the function with this signature (if the match is a multi-key
14493  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14494  * A KeyMap can also handle a string representation of keys.<br />
14495  * Usage:
14496  <pre><code>
14497 // map one key by key code
14498 var map = new Roo.KeyMap("my-element", {
14499     key: 13, // or Roo.EventObject.ENTER
14500     fn: myHandler,
14501     scope: myObject
14502 });
14503
14504 // map multiple keys to one action by string
14505 var map = new Roo.KeyMap("my-element", {
14506     key: "a\r\n\t",
14507     fn: myHandler,
14508     scope: myObject
14509 });
14510
14511 // map multiple keys to multiple actions by strings and array of codes
14512 var map = new Roo.KeyMap("my-element", [
14513     {
14514         key: [10,13],
14515         fn: function(){ alert("Return was pressed"); }
14516     }, {
14517         key: "abc",
14518         fn: function(){ alert('a, b or c was pressed'); }
14519     }, {
14520         key: "\t",
14521         ctrl:true,
14522         shift:true,
14523         fn: function(){ alert('Control + shift + tab was pressed.'); }
14524     }
14525 ]);
14526 </code></pre>
14527  * <b>Note: A KeyMap starts enabled</b>
14528  * @constructor
14529  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14530  * @param {Object} config The config (see {@link #addBinding})
14531  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14532  */
14533 Roo.KeyMap = function(el, config, eventName){
14534     this.el  = Roo.get(el);
14535     this.eventName = eventName || "keydown";
14536     this.bindings = [];
14537     if(config){
14538         this.addBinding(config);
14539     }
14540     this.enable();
14541 };
14542
14543 Roo.KeyMap.prototype = {
14544     /**
14545      * True to stop the event from bubbling and prevent the default browser action if the
14546      * key was handled by the KeyMap (defaults to false)
14547      * @type Boolean
14548      */
14549     stopEvent : false,
14550
14551     /**
14552      * Add a new binding to this KeyMap. The following config object properties are supported:
14553      * <pre>
14554 Property    Type             Description
14555 ----------  ---------------  ----------------------------------------------------------------------
14556 key         String/Array     A single keycode or an array of keycodes to handle
14557 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14558 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14559 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14560 fn          Function         The function to call when KeyMap finds the expected key combination
14561 scope       Object           The scope of the callback function
14562 </pre>
14563      *
14564      * Usage:
14565      * <pre><code>
14566 // Create a KeyMap
14567 var map = new Roo.KeyMap(document, {
14568     key: Roo.EventObject.ENTER,
14569     fn: handleKey,
14570     scope: this
14571 });
14572
14573 //Add a new binding to the existing KeyMap later
14574 map.addBinding({
14575     key: 'abc',
14576     shift: true,
14577     fn: handleKey,
14578     scope: this
14579 });
14580 </code></pre>
14581      * @param {Object/Array} config A single KeyMap config or an array of configs
14582      */
14583         addBinding : function(config){
14584         if(config instanceof Array){
14585             for(var i = 0, len = config.length; i < len; i++){
14586                 this.addBinding(config[i]);
14587             }
14588             return;
14589         }
14590         var keyCode = config.key,
14591             shift = config.shift, 
14592             ctrl = config.ctrl, 
14593             alt = config.alt,
14594             fn = config.fn,
14595             scope = config.scope;
14596         if(typeof keyCode == "string"){
14597             var ks = [];
14598             var keyString = keyCode.toUpperCase();
14599             for(var j = 0, len = keyString.length; j < len; j++){
14600                 ks.push(keyString.charCodeAt(j));
14601             }
14602             keyCode = ks;
14603         }
14604         var keyArray = keyCode instanceof Array;
14605         var handler = function(e){
14606             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14607                 var k = e.getKey();
14608                 if(keyArray){
14609                     for(var i = 0, len = keyCode.length; i < len; i++){
14610                         if(keyCode[i] == k){
14611                           if(this.stopEvent){
14612                               e.stopEvent();
14613                           }
14614                           fn.call(scope || window, k, e);
14615                           return;
14616                         }
14617                     }
14618                 }else{
14619                     if(k == keyCode){
14620                         if(this.stopEvent){
14621                            e.stopEvent();
14622                         }
14623                         fn.call(scope || window, k, e);
14624                     }
14625                 }
14626             }
14627         };
14628         this.bindings.push(handler);  
14629         },
14630
14631     /**
14632      * Shorthand for adding a single key listener
14633      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14634      * following options:
14635      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14636      * @param {Function} fn The function to call
14637      * @param {Object} scope (optional) The scope of the function
14638      */
14639     on : function(key, fn, scope){
14640         var keyCode, shift, ctrl, alt;
14641         if(typeof key == "object" && !(key instanceof Array)){
14642             keyCode = key.key;
14643             shift = key.shift;
14644             ctrl = key.ctrl;
14645             alt = key.alt;
14646         }else{
14647             keyCode = key;
14648         }
14649         this.addBinding({
14650             key: keyCode,
14651             shift: shift,
14652             ctrl: ctrl,
14653             alt: alt,
14654             fn: fn,
14655             scope: scope
14656         })
14657     },
14658
14659     // private
14660     handleKeyDown : function(e){
14661             if(this.enabled){ //just in case
14662             var b = this.bindings;
14663             for(var i = 0, len = b.length; i < len; i++){
14664                 b[i].call(this, e);
14665             }
14666             }
14667         },
14668         
14669         /**
14670          * Returns true if this KeyMap is enabled
14671          * @return {Boolean} 
14672          */
14673         isEnabled : function(){
14674             return this.enabled;  
14675         },
14676         
14677         /**
14678          * Enables this KeyMap
14679          */
14680         enable: function(){
14681                 if(!this.enabled){
14682                     this.el.on(this.eventName, this.handleKeyDown, this);
14683                     this.enabled = true;
14684                 }
14685         },
14686
14687         /**
14688          * Disable this KeyMap
14689          */
14690         disable: function(){
14691                 if(this.enabled){
14692                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14693                     this.enabled = false;
14694                 }
14695         }
14696 };/*
14697  * Based on:
14698  * Ext JS Library 1.1.1
14699  * Copyright(c) 2006-2007, Ext JS, LLC.
14700  *
14701  * Originally Released Under LGPL - original licence link has changed is not relivant.
14702  *
14703  * Fork - LGPL
14704  * <script type="text/javascript">
14705  */
14706
14707  
14708 /**
14709  * @class Roo.util.TextMetrics
14710  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14711  * wide, in pixels, a given block of text will be.
14712  * @singleton
14713  */
14714 Roo.util.TextMetrics = function(){
14715     var shared;
14716     return {
14717         /**
14718          * Measures the size of the specified text
14719          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14720          * that can affect the size of the rendered text
14721          * @param {String} text The text to measure
14722          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14723          * in order to accurately measure the text height
14724          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14725          */
14726         measure : function(el, text, fixedWidth){
14727             if(!shared){
14728                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14729             }
14730             shared.bind(el);
14731             shared.setFixedWidth(fixedWidth || 'auto');
14732             return shared.getSize(text);
14733         },
14734
14735         /**
14736          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14737          * the overhead of multiple calls to initialize the style properties on each measurement.
14738          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14739          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14740          * in order to accurately measure the text height
14741          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14742          */
14743         createInstance : function(el, fixedWidth){
14744             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14745         }
14746     };
14747 }();
14748
14749  
14750
14751 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14752     var ml = new Roo.Element(document.createElement('div'));
14753     document.body.appendChild(ml.dom);
14754     ml.position('absolute');
14755     ml.setLeftTop(-1000, -1000);
14756     ml.hide();
14757
14758     if(fixedWidth){
14759         ml.setWidth(fixedWidth);
14760     }
14761      
14762     var instance = {
14763         /**
14764          * Returns the size of the specified text based on the internal element's style and width properties
14765          * @memberOf Roo.util.TextMetrics.Instance#
14766          * @param {String} text The text to measure
14767          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14768          */
14769         getSize : function(text){
14770             ml.update(text);
14771             var s = ml.getSize();
14772             ml.update('');
14773             return s;
14774         },
14775
14776         /**
14777          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14778          * that can affect the size of the rendered text
14779          * @memberOf Roo.util.TextMetrics.Instance#
14780          * @param {String/HTMLElement} el The element, dom node or id
14781          */
14782         bind : function(el){
14783             ml.setStyle(
14784                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14785             );
14786         },
14787
14788         /**
14789          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14790          * to set a fixed width in order to accurately measure the text height.
14791          * @memberOf Roo.util.TextMetrics.Instance#
14792          * @param {Number} width The width to set on the element
14793          */
14794         setFixedWidth : function(width){
14795             ml.setWidth(width);
14796         },
14797
14798         /**
14799          * Returns the measured width of the specified text
14800          * @memberOf Roo.util.TextMetrics.Instance#
14801          * @param {String} text The text to measure
14802          * @return {Number} width The width in pixels
14803          */
14804         getWidth : function(text){
14805             ml.dom.style.width = 'auto';
14806             return this.getSize(text).width;
14807         },
14808
14809         /**
14810          * Returns the measured height of the specified text.  For multiline text, be sure to call
14811          * {@link #setFixedWidth} if necessary.
14812          * @memberOf Roo.util.TextMetrics.Instance#
14813          * @param {String} text The text to measure
14814          * @return {Number} height The height in pixels
14815          */
14816         getHeight : function(text){
14817             return this.getSize(text).height;
14818         }
14819     };
14820
14821     instance.bind(bindTo);
14822
14823     return instance;
14824 };
14825
14826 // backwards compat
14827 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14828  * Based on:
14829  * Ext JS Library 1.1.1
14830  * Copyright(c) 2006-2007, Ext JS, LLC.
14831  *
14832  * Originally Released Under LGPL - original licence link has changed is not relivant.
14833  *
14834  * Fork - LGPL
14835  * <script type="text/javascript">
14836  */
14837
14838 /**
14839  * @class Roo.state.Provider
14840  * Abstract base class for state provider implementations. This class provides methods
14841  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14842  * Provider interface.
14843  */
14844 Roo.state.Provider = function(){
14845     /**
14846      * @event statechange
14847      * Fires when a state change occurs.
14848      * @param {Provider} this This state provider
14849      * @param {String} key The state key which was changed
14850      * @param {String} value The encoded value for the state
14851      */
14852     this.addEvents({
14853         "statechange": true
14854     });
14855     this.state = {};
14856     Roo.state.Provider.superclass.constructor.call(this);
14857 };
14858 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14859     /**
14860      * Returns the current value for a key
14861      * @param {String} name The key name
14862      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14863      * @return {Mixed} The state data
14864      */
14865     get : function(name, defaultValue){
14866         return typeof this.state[name] == "undefined" ?
14867             defaultValue : this.state[name];
14868     },
14869     
14870     /**
14871      * Clears a value from the state
14872      * @param {String} name The key name
14873      */
14874     clear : function(name){
14875         delete this.state[name];
14876         this.fireEvent("statechange", this, name, null);
14877     },
14878     
14879     /**
14880      * Sets the value for a key
14881      * @param {String} name The key name
14882      * @param {Mixed} value The value to set
14883      */
14884     set : function(name, value){
14885         this.state[name] = value;
14886         this.fireEvent("statechange", this, name, value);
14887     },
14888     
14889     /**
14890      * Decodes a string previously encoded with {@link #encodeValue}.
14891      * @param {String} value The value to decode
14892      * @return {Mixed} The decoded value
14893      */
14894     decodeValue : function(cookie){
14895         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14896         var matches = re.exec(unescape(cookie));
14897         if(!matches || !matches[1]) {
14898             return; // non state cookie
14899         }
14900         var type = matches[1];
14901         var v = matches[2];
14902         switch(type){
14903             case "n":
14904                 return parseFloat(v);
14905             case "d":
14906                 return new Date(Date.parse(v));
14907             case "b":
14908                 return (v == "1");
14909             case "a":
14910                 var all = [];
14911                 var values = v.split("^");
14912                 for(var i = 0, len = values.length; i < len; i++){
14913                     all.push(this.decodeValue(values[i]));
14914                 }
14915                 return all;
14916            case "o":
14917                 var all = {};
14918                 var values = v.split("^");
14919                 for(var i = 0, len = values.length; i < len; i++){
14920                     var kv = values[i].split("=");
14921                     all[kv[0]] = this.decodeValue(kv[1]);
14922                 }
14923                 return all;
14924            default:
14925                 return v;
14926         }
14927     },
14928     
14929     /**
14930      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14931      * @param {Mixed} value The value to encode
14932      * @return {String} The encoded value
14933      */
14934     encodeValue : function(v){
14935         var enc;
14936         if(typeof v == "number"){
14937             enc = "n:" + v;
14938         }else if(typeof v == "boolean"){
14939             enc = "b:" + (v ? "1" : "0");
14940         }else if(v instanceof Date){
14941             enc = "d:" + v.toGMTString();
14942         }else if(v instanceof Array){
14943             var flat = "";
14944             for(var i = 0, len = v.length; i < len; i++){
14945                 flat += this.encodeValue(v[i]);
14946                 if(i != len-1) {
14947                     flat += "^";
14948                 }
14949             }
14950             enc = "a:" + flat;
14951         }else if(typeof v == "object"){
14952             var flat = "";
14953             for(var key in v){
14954                 if(typeof v[key] != "function"){
14955                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14956                 }
14957             }
14958             enc = "o:" + flat.substring(0, flat.length-1);
14959         }else{
14960             enc = "s:" + v;
14961         }
14962         return escape(enc);        
14963     }
14964 });
14965
14966 /*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976 /**
14977  * @class Roo.state.Manager
14978  * This is the global state manager. By default all components that are "state aware" check this class
14979  * for state information if you don't pass them a custom state provider. In order for this class
14980  * to be useful, it must be initialized with a provider when your application initializes.
14981  <pre><code>
14982 // in your initialization function
14983 init : function(){
14984    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14985    ...
14986    // supposed you have a {@link Roo.BorderLayout}
14987    var layout = new Roo.BorderLayout(...);
14988    layout.restoreState();
14989    // or a {Roo.BasicDialog}
14990    var dialog = new Roo.BasicDialog(...);
14991    dialog.restoreState();
14992  </code></pre>
14993  * @singleton
14994  */
14995 Roo.state.Manager = function(){
14996     var provider = new Roo.state.Provider();
14997     
14998     return {
14999         /**
15000          * Configures the default state provider for your application
15001          * @param {Provider} stateProvider The state provider to set
15002          */
15003         setProvider : function(stateProvider){
15004             provider = stateProvider;
15005         },
15006         
15007         /**
15008          * Returns the current value for a key
15009          * @param {String} name The key name
15010          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15011          * @return {Mixed} The state data
15012          */
15013         get : function(key, defaultValue){
15014             return provider.get(key, defaultValue);
15015         },
15016         
15017         /**
15018          * Sets the value for a key
15019          * @param {String} name The key name
15020          * @param {Mixed} value The state data
15021          */
15022          set : function(key, value){
15023             provider.set(key, value);
15024         },
15025         
15026         /**
15027          * Clears a value from the state
15028          * @param {String} name The key name
15029          */
15030         clear : function(key){
15031             provider.clear(key);
15032         },
15033         
15034         /**
15035          * Gets the currently configured state provider
15036          * @return {Provider} The state provider
15037          */
15038         getProvider : function(){
15039             return provider;
15040         }
15041     };
15042 }();
15043 /*
15044  * Based on:
15045  * Ext JS Library 1.1.1
15046  * Copyright(c) 2006-2007, Ext JS, LLC.
15047  *
15048  * Originally Released Under LGPL - original licence link has changed is not relivant.
15049  *
15050  * Fork - LGPL
15051  * <script type="text/javascript">
15052  */
15053 /**
15054  * @class Roo.state.CookieProvider
15055  * @extends Roo.state.Provider
15056  * The default Provider implementation which saves state via cookies.
15057  * <br />Usage:
15058  <pre><code>
15059    var cp = new Roo.state.CookieProvider({
15060        path: "/cgi-bin/",
15061        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15062        domain: "roojs.com"
15063    })
15064    Roo.state.Manager.setProvider(cp);
15065  </code></pre>
15066  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15067  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15068  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15069  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15070  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15071  * domain the page is running on including the 'www' like 'www.roojs.com')
15072  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15073  * @constructor
15074  * Create a new CookieProvider
15075  * @param {Object} config The configuration object
15076  */
15077 Roo.state.CookieProvider = function(config){
15078     Roo.state.CookieProvider.superclass.constructor.call(this);
15079     this.path = "/";
15080     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15081     this.domain = null;
15082     this.secure = false;
15083     Roo.apply(this, config);
15084     this.state = this.readCookies();
15085 };
15086
15087 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15088     // private
15089     set : function(name, value){
15090         if(typeof value == "undefined" || value === null){
15091             this.clear(name);
15092             return;
15093         }
15094         this.setCookie(name, value);
15095         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15096     },
15097
15098     // private
15099     clear : function(name){
15100         this.clearCookie(name);
15101         Roo.state.CookieProvider.superclass.clear.call(this, name);
15102     },
15103
15104     // private
15105     readCookies : function(){
15106         var cookies = {};
15107         var c = document.cookie + ";";
15108         var re = /\s?(.*?)=(.*?);/g;
15109         var matches;
15110         while((matches = re.exec(c)) != null){
15111             var name = matches[1];
15112             var value = matches[2];
15113             if(name && name.substring(0,3) == "ys-"){
15114                 cookies[name.substr(3)] = this.decodeValue(value);
15115             }
15116         }
15117         return cookies;
15118     },
15119
15120     // private
15121     setCookie : function(name, value){
15122         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15123            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15124            ((this.path == null) ? "" : ("; path=" + this.path)) +
15125            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15126            ((this.secure == true) ? "; secure" : "");
15127     },
15128
15129     // private
15130     clearCookie : function(name){
15131         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15132            ((this.path == null) ? "" : ("; path=" + this.path)) +
15133            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15134            ((this.secure == true) ? "; secure" : "");
15135     }
15136 });/*
15137  * Based on:
15138  * Ext JS Library 1.1.1
15139  * Copyright(c) 2006-2007, Ext JS, LLC.
15140  *
15141  * Originally Released Under LGPL - original licence link has changed is not relivant.
15142  *
15143  * Fork - LGPL
15144  * <script type="text/javascript">
15145  */
15146  
15147
15148 /**
15149  * @class Roo.ComponentMgr
15150  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15151  * @singleton
15152  */
15153 Roo.ComponentMgr = function(){
15154     var all = new Roo.util.MixedCollection();
15155
15156     return {
15157         /**
15158          * Registers a component.
15159          * @param {Roo.Component} c The component
15160          */
15161         register : function(c){
15162             all.add(c);
15163         },
15164
15165         /**
15166          * Unregisters a component.
15167          * @param {Roo.Component} c The component
15168          */
15169         unregister : function(c){
15170             all.remove(c);
15171         },
15172
15173         /**
15174          * Returns a component by id
15175          * @param {String} id The component id
15176          */
15177         get : function(id){
15178             return all.get(id);
15179         },
15180
15181         /**
15182          * Registers a function that will be called when a specified component is added to ComponentMgr
15183          * @param {String} id The component id
15184          * @param {Funtction} fn The callback function
15185          * @param {Object} scope The scope of the callback
15186          */
15187         onAvailable : function(id, fn, scope){
15188             all.on("add", function(index, o){
15189                 if(o.id == id){
15190                     fn.call(scope || o, o);
15191                     all.un("add", fn, scope);
15192                 }
15193             });
15194         }
15195     };
15196 }();/*
15197  * Based on:
15198  * Ext JS Library 1.1.1
15199  * Copyright(c) 2006-2007, Ext JS, LLC.
15200  *
15201  * Originally Released Under LGPL - original licence link has changed is not relivant.
15202  *
15203  * Fork - LGPL
15204  * <script type="text/javascript">
15205  */
15206  
15207 /**
15208  * @class Roo.Component
15209  * @extends Roo.util.Observable
15210  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15211  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15212  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15213  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15214  * All visual components (widgets) that require rendering into a layout should subclass Component.
15215  * @constructor
15216  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15217  * 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
15218  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15219  */
15220 Roo.Component = function(config){
15221     config = config || {};
15222     if(config.tagName || config.dom || typeof config == "string"){ // element object
15223         config = {el: config, id: config.id || config};
15224     }
15225     this.initialConfig = config;
15226
15227     Roo.apply(this, config);
15228     this.addEvents({
15229         /**
15230          * @event disable
15231          * Fires after the component is disabled.
15232              * @param {Roo.Component} this
15233              */
15234         disable : true,
15235         /**
15236          * @event enable
15237          * Fires after the component is enabled.
15238              * @param {Roo.Component} this
15239              */
15240         enable : true,
15241         /**
15242          * @event beforeshow
15243          * Fires before the component is shown.  Return false to stop the show.
15244              * @param {Roo.Component} this
15245              */
15246         beforeshow : true,
15247         /**
15248          * @event show
15249          * Fires after the component is shown.
15250              * @param {Roo.Component} this
15251              */
15252         show : true,
15253         /**
15254          * @event beforehide
15255          * Fires before the component is hidden. Return false to stop the hide.
15256              * @param {Roo.Component} this
15257              */
15258         beforehide : true,
15259         /**
15260          * @event hide
15261          * Fires after the component is hidden.
15262              * @param {Roo.Component} this
15263              */
15264         hide : true,
15265         /**
15266          * @event beforerender
15267          * Fires before the component is rendered. Return false to stop the render.
15268              * @param {Roo.Component} this
15269              */
15270         beforerender : true,
15271         /**
15272          * @event render
15273          * Fires after the component is rendered.
15274              * @param {Roo.Component} this
15275              */
15276         render : true,
15277         /**
15278          * @event beforedestroy
15279          * Fires before the component is destroyed. Return false to stop the destroy.
15280              * @param {Roo.Component} this
15281              */
15282         beforedestroy : true,
15283         /**
15284          * @event destroy
15285          * Fires after the component is destroyed.
15286              * @param {Roo.Component} this
15287              */
15288         destroy : true
15289     });
15290     if(!this.id){
15291         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15292     }
15293     Roo.ComponentMgr.register(this);
15294     Roo.Component.superclass.constructor.call(this);
15295     this.initComponent();
15296     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15297         this.render(this.renderTo);
15298         delete this.renderTo;
15299     }
15300 };
15301
15302 /** @private */
15303 Roo.Component.AUTO_ID = 1000;
15304
15305 Roo.extend(Roo.Component, Roo.util.Observable, {
15306     /**
15307      * @scope Roo.Component.prototype
15308      * @type {Boolean}
15309      * true if this component is hidden. Read-only.
15310      */
15311     hidden : false,
15312     /**
15313      * @type {Boolean}
15314      * true if this component is disabled. Read-only.
15315      */
15316     disabled : false,
15317     /**
15318      * @type {Boolean}
15319      * true if this component has been rendered. Read-only.
15320      */
15321     rendered : false,
15322     
15323     /** @cfg {String} disableClass
15324      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15325      */
15326     disabledClass : "x-item-disabled",
15327         /** @cfg {Boolean} allowDomMove
15328          * Whether the component can move the Dom node when rendering (defaults to true).
15329          */
15330     allowDomMove : true,
15331     /** @cfg {String} hideMode (display|visibility)
15332      * How this component should hidden. Supported values are
15333      * "visibility" (css visibility), "offsets" (negative offset position) and
15334      * "display" (css display) - defaults to "display".
15335      */
15336     hideMode: 'display',
15337
15338     /** @private */
15339     ctype : "Roo.Component",
15340
15341     /**
15342      * @cfg {String} actionMode 
15343      * which property holds the element that used for  hide() / show() / disable() / enable()
15344      * default is 'el' 
15345      */
15346     actionMode : "el",
15347
15348     /** @private */
15349     getActionEl : function(){
15350         return this[this.actionMode];
15351     },
15352
15353     initComponent : Roo.emptyFn,
15354     /**
15355      * If this is a lazy rendering component, render it to its container element.
15356      * @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.
15357      */
15358     render : function(container, position){
15359         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15360             if(!container && this.el){
15361                 this.el = Roo.get(this.el);
15362                 container = this.el.dom.parentNode;
15363                 this.allowDomMove = false;
15364             }
15365             this.container = Roo.get(container);
15366             this.rendered = true;
15367             if(position !== undefined){
15368                 if(typeof position == 'number'){
15369                     position = this.container.dom.childNodes[position];
15370                 }else{
15371                     position = Roo.getDom(position);
15372                 }
15373             }
15374             this.onRender(this.container, position || null);
15375             if(this.cls){
15376                 this.el.addClass(this.cls);
15377                 delete this.cls;
15378             }
15379             if(this.style){
15380                 this.el.applyStyles(this.style);
15381                 delete this.style;
15382             }
15383             this.fireEvent("render", this);
15384             this.afterRender(this.container);
15385             if(this.hidden){
15386                 this.hide();
15387             }
15388             if(this.disabled){
15389                 this.disable();
15390             }
15391         }
15392         return this;
15393     },
15394
15395     /** @private */
15396     // default function is not really useful
15397     onRender : function(ct, position){
15398         if(this.el){
15399             this.el = Roo.get(this.el);
15400             if(this.allowDomMove !== false){
15401                 ct.dom.insertBefore(this.el.dom, position);
15402             }
15403         }
15404     },
15405
15406     /** @private */
15407     getAutoCreate : function(){
15408         var cfg = typeof this.autoCreate == "object" ?
15409                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15410         if(this.id && !cfg.id){
15411             cfg.id = this.id;
15412         }
15413         return cfg;
15414     },
15415
15416     /** @private */
15417     afterRender : Roo.emptyFn,
15418
15419     /**
15420      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15421      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15422      */
15423     destroy : function(){
15424         if(this.fireEvent("beforedestroy", this) !== false){
15425             this.purgeListeners();
15426             this.beforeDestroy();
15427             if(this.rendered){
15428                 this.el.removeAllListeners();
15429                 this.el.remove();
15430                 if(this.actionMode == "container"){
15431                     this.container.remove();
15432                 }
15433             }
15434             this.onDestroy();
15435             Roo.ComponentMgr.unregister(this);
15436             this.fireEvent("destroy", this);
15437         }
15438     },
15439
15440         /** @private */
15441     beforeDestroy : function(){
15442
15443     },
15444
15445         /** @private */
15446         onDestroy : function(){
15447
15448     },
15449
15450     /**
15451      * Returns the underlying {@link Roo.Element}.
15452      * @return {Roo.Element} The element
15453      */
15454     getEl : function(){
15455         return this.el;
15456     },
15457
15458     /**
15459      * Returns the id of this component.
15460      * @return {String}
15461      */
15462     getId : function(){
15463         return this.id;
15464     },
15465
15466     /**
15467      * Try to focus this component.
15468      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15469      * @return {Roo.Component} this
15470      */
15471     focus : function(selectText){
15472         if(this.rendered){
15473             this.el.focus();
15474             if(selectText === true){
15475                 this.el.dom.select();
15476             }
15477         }
15478         return this;
15479     },
15480
15481     /** @private */
15482     blur : function(){
15483         if(this.rendered){
15484             this.el.blur();
15485         }
15486         return this;
15487     },
15488
15489     /**
15490      * Disable this component.
15491      * @return {Roo.Component} this
15492      */
15493     disable : function(){
15494         if(this.rendered){
15495             this.onDisable();
15496         }
15497         this.disabled = true;
15498         this.fireEvent("disable", this);
15499         return this;
15500     },
15501
15502         // private
15503     onDisable : function(){
15504         this.getActionEl().addClass(this.disabledClass);
15505         this.el.dom.disabled = true;
15506     },
15507
15508     /**
15509      * Enable this component.
15510      * @return {Roo.Component} this
15511      */
15512     enable : function(){
15513         if(this.rendered){
15514             this.onEnable();
15515         }
15516         this.disabled = false;
15517         this.fireEvent("enable", this);
15518         return this;
15519     },
15520
15521         // private
15522     onEnable : function(){
15523         this.getActionEl().removeClass(this.disabledClass);
15524         this.el.dom.disabled = false;
15525     },
15526
15527     /**
15528      * Convenience function for setting disabled/enabled by boolean.
15529      * @param {Boolean} disabled
15530      */
15531     setDisabled : function(disabled){
15532         this[disabled ? "disable" : "enable"]();
15533     },
15534
15535     /**
15536      * Show this component.
15537      * @return {Roo.Component} this
15538      */
15539     show: function(){
15540         if(this.fireEvent("beforeshow", this) !== false){
15541             this.hidden = false;
15542             if(this.rendered){
15543                 this.onShow();
15544             }
15545             this.fireEvent("show", this);
15546         }
15547         return this;
15548     },
15549
15550     // private
15551     onShow : function(){
15552         var ae = this.getActionEl();
15553         if(this.hideMode == 'visibility'){
15554             ae.dom.style.visibility = "visible";
15555         }else if(this.hideMode == 'offsets'){
15556             ae.removeClass('x-hidden');
15557         }else{
15558             ae.dom.style.display = "";
15559         }
15560     },
15561
15562     /**
15563      * Hide this component.
15564      * @return {Roo.Component} this
15565      */
15566     hide: function(){
15567         if(this.fireEvent("beforehide", this) !== false){
15568             this.hidden = true;
15569             if(this.rendered){
15570                 this.onHide();
15571             }
15572             this.fireEvent("hide", this);
15573         }
15574         return this;
15575     },
15576
15577     // private
15578     onHide : function(){
15579         var ae = this.getActionEl();
15580         if(this.hideMode == 'visibility'){
15581             ae.dom.style.visibility = "hidden";
15582         }else if(this.hideMode == 'offsets'){
15583             ae.addClass('x-hidden');
15584         }else{
15585             ae.dom.style.display = "none";
15586         }
15587     },
15588
15589     /**
15590      * Convenience function to hide or show this component by boolean.
15591      * @param {Boolean} visible True to show, false to hide
15592      * @return {Roo.Component} this
15593      */
15594     setVisible: function(visible){
15595         if(visible) {
15596             this.show();
15597         }else{
15598             this.hide();
15599         }
15600         return this;
15601     },
15602
15603     /**
15604      * Returns true if this component is visible.
15605      */
15606     isVisible : function(){
15607         return this.getActionEl().isVisible();
15608     },
15609
15610     cloneConfig : function(overrides){
15611         overrides = overrides || {};
15612         var id = overrides.id || Roo.id();
15613         var cfg = Roo.applyIf(overrides, this.initialConfig);
15614         cfg.id = id; // prevent dup id
15615         return new this.constructor(cfg);
15616     }
15617 });/*
15618  * Based on:
15619  * Ext JS Library 1.1.1
15620  * Copyright(c) 2006-2007, Ext JS, LLC.
15621  *
15622  * Originally Released Under LGPL - original licence link has changed is not relivant.
15623  *
15624  * Fork - LGPL
15625  * <script type="text/javascript">
15626  */
15627
15628 /**
15629  * @class Roo.BoxComponent
15630  * @extends Roo.Component
15631  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15632  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15633  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15634  * layout containers.
15635  * @constructor
15636  * @param {Roo.Element/String/Object} config The configuration options.
15637  */
15638 Roo.BoxComponent = function(config){
15639     Roo.Component.call(this, config);
15640     this.addEvents({
15641         /**
15642          * @event resize
15643          * Fires after the component is resized.
15644              * @param {Roo.Component} this
15645              * @param {Number} adjWidth The box-adjusted width that was set
15646              * @param {Number} adjHeight The box-adjusted height that was set
15647              * @param {Number} rawWidth The width that was originally specified
15648              * @param {Number} rawHeight The height that was originally specified
15649              */
15650         resize : true,
15651         /**
15652          * @event move
15653          * Fires after the component is moved.
15654              * @param {Roo.Component} this
15655              * @param {Number} x The new x position
15656              * @param {Number} y The new y position
15657              */
15658         move : true
15659     });
15660 };
15661
15662 Roo.extend(Roo.BoxComponent, Roo.Component, {
15663     // private, set in afterRender to signify that the component has been rendered
15664     boxReady : false,
15665     // private, used to defer height settings to subclasses
15666     deferHeight: false,
15667     /** @cfg {Number} width
15668      * width (optional) size of component
15669      */
15670      /** @cfg {Number} height
15671      * height (optional) size of component
15672      */
15673      
15674     /**
15675      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15676      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15677      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15678      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15679      * @return {Roo.BoxComponent} this
15680      */
15681     setSize : function(w, h){
15682         // support for standard size objects
15683         if(typeof w == 'object'){
15684             h = w.height;
15685             w = w.width;
15686         }
15687         // not rendered
15688         if(!this.boxReady){
15689             this.width = w;
15690             this.height = h;
15691             return this;
15692         }
15693
15694         // prevent recalcs when not needed
15695         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15696             return this;
15697         }
15698         this.lastSize = {width: w, height: h};
15699
15700         var adj = this.adjustSize(w, h);
15701         var aw = adj.width, ah = adj.height;
15702         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15703             var rz = this.getResizeEl();
15704             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15705                 rz.setSize(aw, ah);
15706             }else if(!this.deferHeight && ah !== undefined){
15707                 rz.setHeight(ah);
15708             }else if(aw !== undefined){
15709                 rz.setWidth(aw);
15710             }
15711             this.onResize(aw, ah, w, h);
15712             this.fireEvent('resize', this, aw, ah, w, h);
15713         }
15714         return this;
15715     },
15716
15717     /**
15718      * Gets the current size of the component's underlying element.
15719      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15720      */
15721     getSize : function(){
15722         return this.el.getSize();
15723     },
15724
15725     /**
15726      * Gets the current XY position of the component's underlying element.
15727      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15728      * @return {Array} The XY position of the element (e.g., [100, 200])
15729      */
15730     getPosition : function(local){
15731         if(local === true){
15732             return [this.el.getLeft(true), this.el.getTop(true)];
15733         }
15734         return this.xy || this.el.getXY();
15735     },
15736
15737     /**
15738      * Gets the current box measurements of the component's underlying element.
15739      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15740      * @returns {Object} box An object in the format {x, y, width, height}
15741      */
15742     getBox : function(local){
15743         var s = this.el.getSize();
15744         if(local){
15745             s.x = this.el.getLeft(true);
15746             s.y = this.el.getTop(true);
15747         }else{
15748             var xy = this.xy || this.el.getXY();
15749             s.x = xy[0];
15750             s.y = xy[1];
15751         }
15752         return s;
15753     },
15754
15755     /**
15756      * Sets the current box measurements of the component's underlying element.
15757      * @param {Object} box An object in the format {x, y, width, height}
15758      * @returns {Roo.BoxComponent} this
15759      */
15760     updateBox : function(box){
15761         this.setSize(box.width, box.height);
15762         this.setPagePosition(box.x, box.y);
15763         return this;
15764     },
15765
15766     // protected
15767     getResizeEl : function(){
15768         return this.resizeEl || this.el;
15769     },
15770
15771     // protected
15772     getPositionEl : function(){
15773         return this.positionEl || this.el;
15774     },
15775
15776     /**
15777      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15778      * This method fires the move event.
15779      * @param {Number} left The new left
15780      * @param {Number} top The new top
15781      * @returns {Roo.BoxComponent} this
15782      */
15783     setPosition : function(x, y){
15784         this.x = x;
15785         this.y = y;
15786         if(!this.boxReady){
15787             return this;
15788         }
15789         var adj = this.adjustPosition(x, y);
15790         var ax = adj.x, ay = adj.y;
15791
15792         var el = this.getPositionEl();
15793         if(ax !== undefined || ay !== undefined){
15794             if(ax !== undefined && ay !== undefined){
15795                 el.setLeftTop(ax, ay);
15796             }else if(ax !== undefined){
15797                 el.setLeft(ax);
15798             }else if(ay !== undefined){
15799                 el.setTop(ay);
15800             }
15801             this.onPosition(ax, ay);
15802             this.fireEvent('move', this, ax, ay);
15803         }
15804         return this;
15805     },
15806
15807     /**
15808      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15809      * This method fires the move event.
15810      * @param {Number} x The new x position
15811      * @param {Number} y The new y position
15812      * @returns {Roo.BoxComponent} this
15813      */
15814     setPagePosition : function(x, y){
15815         this.pageX = x;
15816         this.pageY = y;
15817         if(!this.boxReady){
15818             return;
15819         }
15820         if(x === undefined || y === undefined){ // cannot translate undefined points
15821             return;
15822         }
15823         var p = this.el.translatePoints(x, y);
15824         this.setPosition(p.left, p.top);
15825         return this;
15826     },
15827
15828     // private
15829     onRender : function(ct, position){
15830         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15831         if(this.resizeEl){
15832             this.resizeEl = Roo.get(this.resizeEl);
15833         }
15834         if(this.positionEl){
15835             this.positionEl = Roo.get(this.positionEl);
15836         }
15837     },
15838
15839     // private
15840     afterRender : function(){
15841         Roo.BoxComponent.superclass.afterRender.call(this);
15842         this.boxReady = true;
15843         this.setSize(this.width, this.height);
15844         if(this.x || this.y){
15845             this.setPosition(this.x, this.y);
15846         }
15847         if(this.pageX || this.pageY){
15848             this.setPagePosition(this.pageX, this.pageY);
15849         }
15850     },
15851
15852     /**
15853      * Force the component's size to recalculate based on the underlying element's current height and width.
15854      * @returns {Roo.BoxComponent} this
15855      */
15856     syncSize : function(){
15857         delete this.lastSize;
15858         this.setSize(this.el.getWidth(), this.el.getHeight());
15859         return this;
15860     },
15861
15862     /**
15863      * Called after the component is resized, this method is empty by default but can be implemented by any
15864      * subclass that needs to perform custom logic after a resize occurs.
15865      * @param {Number} adjWidth The box-adjusted width that was set
15866      * @param {Number} adjHeight The box-adjusted height that was set
15867      * @param {Number} rawWidth The width that was originally specified
15868      * @param {Number} rawHeight The height that was originally specified
15869      */
15870     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15871
15872     },
15873
15874     /**
15875      * Called after the component is moved, this method is empty by default but can be implemented by any
15876      * subclass that needs to perform custom logic after a move occurs.
15877      * @param {Number} x The new x position
15878      * @param {Number} y The new y position
15879      */
15880     onPosition : function(x, y){
15881
15882     },
15883
15884     // private
15885     adjustSize : function(w, h){
15886         if(this.autoWidth){
15887             w = 'auto';
15888         }
15889         if(this.autoHeight){
15890             h = 'auto';
15891         }
15892         return {width : w, height: h};
15893     },
15894
15895     // private
15896     adjustPosition : function(x, y){
15897         return {x : x, y: y};
15898     }
15899 });/*
15900  * Original code for Roojs - LGPL
15901  * <script type="text/javascript">
15902  */
15903  
15904 /**
15905  * @class Roo.XComponent
15906  * A delayed Element creator...
15907  * Or a way to group chunks of interface together.
15908  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15909  *  used in conjunction with XComponent.build() it will create an instance of each element,
15910  *  then call addxtype() to build the User interface.
15911  * 
15912  * Mypart.xyx = new Roo.XComponent({
15913
15914     parent : 'Mypart.xyz', // empty == document.element.!!
15915     order : '001',
15916     name : 'xxxx'
15917     region : 'xxxx'
15918     disabled : function() {} 
15919      
15920     tree : function() { // return an tree of xtype declared components
15921         var MODULE = this;
15922         return 
15923         {
15924             xtype : 'NestedLayoutPanel',
15925             // technicall
15926         }
15927      ]
15928  *})
15929  *
15930  *
15931  * It can be used to build a big heiracy, with parent etc.
15932  * or you can just use this to render a single compoent to a dom element
15933  * MYPART.render(Roo.Element | String(id) | dom_element )
15934  *
15935  *
15936  * Usage patterns.
15937  *
15938  * Classic Roo
15939  *
15940  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15941  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15942  *
15943  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15944  *
15945  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15946  * - if mulitple topModules exist, the last one is defined as the top module.
15947  *
15948  * Embeded Roo
15949  * 
15950  * When the top level or multiple modules are to embedded into a existing HTML page,
15951  * the parent element can container '#id' of the element where the module will be drawn.
15952  *
15953  * Bootstrap Roo
15954  *
15955  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15956  * it relies more on a include mechanism, where sub modules are included into an outer page.
15957  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15958  * 
15959  * Bootstrap Roo Included elements
15960  *
15961  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15962  * hence confusing the component builder as it thinks there are multiple top level elements. 
15963  *
15964  * 
15965  * 
15966  * @extends Roo.util.Observable
15967  * @constructor
15968  * @param cfg {Object} configuration of component
15969  * 
15970  */
15971 Roo.XComponent = function(cfg) {
15972     Roo.apply(this, cfg);
15973     this.addEvents({ 
15974         /**
15975              * @event built
15976              * Fires when this the componnt is built
15977              * @param {Roo.XComponent} c the component
15978              */
15979         'built' : true
15980         
15981     });
15982     this.region = this.region || 'center'; // default..
15983     Roo.XComponent.register(this);
15984     this.modules = false;
15985     this.el = false; // where the layout goes..
15986     
15987     
15988 }
15989 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15990     /**
15991      * @property el
15992      * The created element (with Roo.factory())
15993      * @type {Roo.Layout}
15994      */
15995     el  : false,
15996     
15997     /**
15998      * @property el
15999      * for BC  - use el in new code
16000      * @type {Roo.Layout}
16001      */
16002     panel : false,
16003     
16004     /**
16005      * @property layout
16006      * for BC  - use el in new code
16007      * @type {Roo.Layout}
16008      */
16009     layout : false,
16010     
16011      /**
16012      * @cfg {Function|boolean} disabled
16013      * If this module is disabled by some rule, return true from the funtion
16014      */
16015     disabled : false,
16016     
16017     /**
16018      * @cfg {String} parent 
16019      * Name of parent element which it get xtype added to..
16020      */
16021     parent: false,
16022     
16023     /**
16024      * @cfg {String} order
16025      * Used to set the order in which elements are created (usefull for multiple tabs)
16026      */
16027     
16028     order : false,
16029     /**
16030      * @cfg {String} name
16031      * String to display while loading.
16032      */
16033     name : false,
16034     /**
16035      * @cfg {String} region
16036      * Region to render component to (defaults to center)
16037      */
16038     region : 'center',
16039     
16040     /**
16041      * @cfg {Array} items
16042      * A single item array - the first element is the root of the tree..
16043      * It's done this way to stay compatible with the Xtype system...
16044      */
16045     items : false,
16046     
16047     /**
16048      * @property _tree
16049      * The method that retuns the tree of parts that make up this compoennt 
16050      * @type {function}
16051      */
16052     _tree  : false,
16053     
16054      /**
16055      * render
16056      * render element to dom or tree
16057      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16058      */
16059     
16060     render : function(el)
16061     {
16062         
16063         el = el || false;
16064         var hp = this.parent ? 1 : 0;
16065         Roo.debug &&  Roo.log(this);
16066         
16067         var tree = this._tree ? this._tree() : this.tree();
16068
16069         
16070         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16071             // if parent is a '#.....' string, then let's use that..
16072             var ename = this.parent.substr(1);
16073             this.parent = false;
16074             Roo.debug && Roo.log(ename);
16075             switch (ename) {
16076                 case 'bootstrap-body':
16077                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16078                         // this is the BorderLayout standard?
16079                        this.parent = { el : true };
16080                        break;
16081                     }
16082                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16083                         // need to insert stuff...
16084                         this.parent =  {
16085                              el : new Roo.bootstrap.layout.Border({
16086                                  el : document.body, 
16087                      
16088                                  center: {
16089                                     titlebar: false,
16090                                     autoScroll:false,
16091                                     closeOnTab: true,
16092                                     tabPosition: 'top',
16093                                       //resizeTabs: true,
16094                                     alwaysShowTabs: true,
16095                                     hideTabs: false
16096                                      //minTabWidth: 140
16097                                  }
16098                              })
16099                         
16100                          };
16101                          break;
16102                     }
16103                          
16104                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16105                         this.parent = { el :  new  Roo.bootstrap.Body() };
16106                         Roo.debug && Roo.log("setting el to doc body");
16107                          
16108                     } else {
16109                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16110                     }
16111                     break;
16112                 case 'bootstrap':
16113                     this.parent = { el : true};
16114                     // fall through
16115                 default:
16116                     el = Roo.get(ename);
16117                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16118                         this.parent = { el : true};
16119                     }
16120                     
16121                     break;
16122             }
16123                 
16124             
16125             if (!el && !this.parent) {
16126                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16127                 return;
16128             }
16129         }
16130         
16131         Roo.debug && Roo.log("EL:");
16132         Roo.debug && Roo.log(el);
16133         Roo.debug && Roo.log("this.parent.el:");
16134         Roo.debug && Roo.log(this.parent.el);
16135         
16136
16137         // altertive root elements ??? - we need a better way to indicate these.
16138         var is_alt = Roo.XComponent.is_alt ||
16139                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16140                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16141                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16142         
16143         
16144         
16145         if (!this.parent && is_alt) {
16146             //el = Roo.get(document.body);
16147             this.parent = { el : true };
16148         }
16149             
16150             
16151         
16152         if (!this.parent) {
16153             
16154             Roo.debug && Roo.log("no parent - creating one");
16155             
16156             el = el ? Roo.get(el) : false;      
16157             
16158             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16159                 
16160                 this.parent =  {
16161                     el : new Roo.bootstrap.layout.Border({
16162                         el: el || document.body,
16163                     
16164                         center: {
16165                             titlebar: false,
16166                             autoScroll:false,
16167                             closeOnTab: true,
16168                             tabPosition: 'top',
16169                              //resizeTabs: true,
16170                             alwaysShowTabs: false,
16171                             hideTabs: true,
16172                             minTabWidth: 140,
16173                             overflow: 'visible'
16174                          }
16175                      })
16176                 };
16177             } else {
16178             
16179                 // it's a top level one..
16180                 this.parent =  {
16181                     el : new Roo.BorderLayout(el || document.body, {
16182                         center: {
16183                             titlebar: false,
16184                             autoScroll:false,
16185                             closeOnTab: true,
16186                             tabPosition: 'top',
16187                              //resizeTabs: true,
16188                             alwaysShowTabs: el && hp? false :  true,
16189                             hideTabs: el || !hp ? true :  false,
16190                             minTabWidth: 140
16191                          }
16192                     })
16193                 };
16194             }
16195         }
16196         
16197         if (!this.parent.el) {
16198                 // probably an old style ctor, which has been disabled.
16199                 return;
16200
16201         }
16202                 // The 'tree' method is  '_tree now' 
16203             
16204         tree.region = tree.region || this.region;
16205         var is_body = false;
16206         if (this.parent.el === true) {
16207             // bootstrap... - body..
16208             if (el) {
16209                 tree.el = el;
16210             }
16211             this.parent.el = Roo.factory(tree);
16212             is_body = true;
16213         }
16214         
16215         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16216         this.fireEvent('built', this);
16217         
16218         this.panel = this.el;
16219         this.layout = this.panel.layout;
16220         this.parentLayout = this.parent.layout  || false;  
16221          
16222     }
16223     
16224 });
16225
16226 Roo.apply(Roo.XComponent, {
16227     /**
16228      * @property  hideProgress
16229      * true to disable the building progress bar.. usefull on single page renders.
16230      * @type Boolean
16231      */
16232     hideProgress : false,
16233     /**
16234      * @property  buildCompleted
16235      * True when the builder has completed building the interface.
16236      * @type Boolean
16237      */
16238     buildCompleted : false,
16239      
16240     /**
16241      * @property  topModule
16242      * the upper most module - uses document.element as it's constructor.
16243      * @type Object
16244      */
16245      
16246     topModule  : false,
16247       
16248     /**
16249      * @property  modules
16250      * array of modules to be created by registration system.
16251      * @type {Array} of Roo.XComponent
16252      */
16253     
16254     modules : [],
16255     /**
16256      * @property  elmodules
16257      * array of modules to be created by which use #ID 
16258      * @type {Array} of Roo.XComponent
16259      */
16260      
16261     elmodules : [],
16262
16263      /**
16264      * @property  is_alt
16265      * Is an alternative Root - normally used by bootstrap or other systems,
16266      *    where the top element in the tree can wrap 'body' 
16267      * @type {boolean}  (default false)
16268      */
16269      
16270     is_alt : false,
16271     /**
16272      * @property  build_from_html
16273      * Build elements from html - used by bootstrap HTML stuff 
16274      *    - this is cleared after build is completed
16275      * @type {boolean}    (default false)
16276      */
16277      
16278     build_from_html : false,
16279     /**
16280      * Register components to be built later.
16281      *
16282      * This solves the following issues
16283      * - Building is not done on page load, but after an authentication process has occured.
16284      * - Interface elements are registered on page load
16285      * - Parent Interface elements may not be loaded before child, so this handles that..
16286      * 
16287      *
16288      * example:
16289      * 
16290      * MyApp.register({
16291           order : '000001',
16292           module : 'Pman.Tab.projectMgr',
16293           region : 'center',
16294           parent : 'Pman.layout',
16295           disabled : false,  // or use a function..
16296         })
16297      
16298      * * @param {Object} details about module
16299      */
16300     register : function(obj) {
16301                 
16302         Roo.XComponent.event.fireEvent('register', obj);
16303         switch(typeof(obj.disabled) ) {
16304                 
16305             case 'undefined':
16306                 break;
16307             
16308             case 'function':
16309                 if ( obj.disabled() ) {
16310                         return;
16311                 }
16312                 break;
16313             
16314             default:
16315                 if (obj.disabled) {
16316                         return;
16317                 }
16318                 break;
16319         }
16320                 
16321         this.modules.push(obj);
16322          
16323     },
16324     /**
16325      * convert a string to an object..
16326      * eg. 'AAA.BBB' -> finds AAA.BBB
16327
16328      */
16329     
16330     toObject : function(str)
16331     {
16332         if (!str || typeof(str) == 'object') {
16333             return str;
16334         }
16335         if (str.substring(0,1) == '#') {
16336             return str;
16337         }
16338
16339         var ar = str.split('.');
16340         var rt, o;
16341         rt = ar.shift();
16342             /** eval:var:o */
16343         try {
16344             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16345         } catch (e) {
16346             throw "Module not found : " + str;
16347         }
16348         
16349         if (o === false) {
16350             throw "Module not found : " + str;
16351         }
16352         Roo.each(ar, function(e) {
16353             if (typeof(o[e]) == 'undefined') {
16354                 throw "Module not found : " + str;
16355             }
16356             o = o[e];
16357         });
16358         
16359         return o;
16360         
16361     },
16362     
16363     
16364     /**
16365      * move modules into their correct place in the tree..
16366      * 
16367      */
16368     preBuild : function ()
16369     {
16370         var _t = this;
16371         Roo.each(this.modules , function (obj)
16372         {
16373             Roo.XComponent.event.fireEvent('beforebuild', obj);
16374             
16375             var opar = obj.parent;
16376             try { 
16377                 obj.parent = this.toObject(opar);
16378             } catch(e) {
16379                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16380                 return;
16381             }
16382             
16383             if (!obj.parent) {
16384                 Roo.debug && Roo.log("GOT top level module");
16385                 Roo.debug && Roo.log(obj);
16386                 obj.modules = new Roo.util.MixedCollection(false, 
16387                     function(o) { return o.order + '' }
16388                 );
16389                 this.topModule = obj;
16390                 return;
16391             }
16392                         // parent is a string (usually a dom element name..)
16393             if (typeof(obj.parent) == 'string') {
16394                 this.elmodules.push(obj);
16395                 return;
16396             }
16397             if (obj.parent.constructor != Roo.XComponent) {
16398                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16399             }
16400             if (!obj.parent.modules) {
16401                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16402                     function(o) { return o.order + '' }
16403                 );
16404             }
16405             if (obj.parent.disabled) {
16406                 obj.disabled = true;
16407             }
16408             obj.parent.modules.add(obj);
16409         }, this);
16410     },
16411     
16412      /**
16413      * make a list of modules to build.
16414      * @return {Array} list of modules. 
16415      */ 
16416     
16417     buildOrder : function()
16418     {
16419         var _this = this;
16420         var cmp = function(a,b) {   
16421             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16422         };
16423         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16424             throw "No top level modules to build";
16425         }
16426         
16427         // make a flat list in order of modules to build.
16428         var mods = this.topModule ? [ this.topModule ] : [];
16429                 
16430         
16431         // elmodules (is a list of DOM based modules )
16432         Roo.each(this.elmodules, function(e) {
16433             mods.push(e);
16434             if (!this.topModule &&
16435                 typeof(e.parent) == 'string' &&
16436                 e.parent.substring(0,1) == '#' &&
16437                 Roo.get(e.parent.substr(1))
16438                ) {
16439                 
16440                 _this.topModule = e;
16441             }
16442             
16443         });
16444
16445         
16446         // add modules to their parents..
16447         var addMod = function(m) {
16448             Roo.debug && Roo.log("build Order: add: " + m.name);
16449                 
16450             mods.push(m);
16451             if (m.modules && !m.disabled) {
16452                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16453                 m.modules.keySort('ASC',  cmp );
16454                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16455     
16456                 m.modules.each(addMod);
16457             } else {
16458                 Roo.debug && Roo.log("build Order: no child modules");
16459             }
16460             // not sure if this is used any more..
16461             if (m.finalize) {
16462                 m.finalize.name = m.name + " (clean up) ";
16463                 mods.push(m.finalize);
16464             }
16465             
16466         }
16467         if (this.topModule && this.topModule.modules) { 
16468             this.topModule.modules.keySort('ASC',  cmp );
16469             this.topModule.modules.each(addMod);
16470         } 
16471         return mods;
16472     },
16473     
16474      /**
16475      * Build the registered modules.
16476      * @param {Object} parent element.
16477      * @param {Function} optional method to call after module has been added.
16478      * 
16479      */ 
16480    
16481     build : function(opts) 
16482     {
16483         
16484         if (typeof(opts) != 'undefined') {
16485             Roo.apply(this,opts);
16486         }
16487         
16488         this.preBuild();
16489         var mods = this.buildOrder();
16490       
16491         //this.allmods = mods;
16492         //Roo.debug && Roo.log(mods);
16493         //return;
16494         if (!mods.length) { // should not happen
16495             throw "NO modules!!!";
16496         }
16497         
16498         
16499         var msg = "Building Interface...";
16500         // flash it up as modal - so we store the mask!?
16501         if (!this.hideProgress && Roo.MessageBox) {
16502             Roo.MessageBox.show({ title: 'loading' });
16503             Roo.MessageBox.show({
16504                title: "Please wait...",
16505                msg: msg,
16506                width:450,
16507                progress:true,
16508                closable:false,
16509                modal: false
16510               
16511             });
16512         }
16513         var total = mods.length;
16514         
16515         var _this = this;
16516         var progressRun = function() {
16517             if (!mods.length) {
16518                 Roo.debug && Roo.log('hide?');
16519                 if (!this.hideProgress && Roo.MessageBox) {
16520                     Roo.MessageBox.hide();
16521                 }
16522                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16523                 
16524                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16525                 
16526                 // THE END...
16527                 return false;   
16528             }
16529             
16530             var m = mods.shift();
16531             
16532             
16533             Roo.debug && Roo.log(m);
16534             // not sure if this is supported any more.. - modules that are are just function
16535             if (typeof(m) == 'function') { 
16536                 m.call(this);
16537                 return progressRun.defer(10, _this);
16538             } 
16539             
16540             
16541             msg = "Building Interface " + (total  - mods.length) + 
16542                     " of " + total + 
16543                     (m.name ? (' - ' + m.name) : '');
16544                         Roo.debug && Roo.log(msg);
16545             if (!_this.hideProgress &&  Roo.MessageBox) { 
16546                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16547             }
16548             
16549          
16550             // is the module disabled?
16551             var disabled = (typeof(m.disabled) == 'function') ?
16552                 m.disabled.call(m.module.disabled) : m.disabled;    
16553             
16554             
16555             if (disabled) {
16556                 return progressRun(); // we do not update the display!
16557             }
16558             
16559             // now build 
16560             
16561                         
16562                         
16563             m.render();
16564             // it's 10 on top level, and 1 on others??? why...
16565             return progressRun.defer(10, _this);
16566              
16567         }
16568         progressRun.defer(1, _this);
16569      
16570         
16571         
16572     },
16573         
16574         
16575         /**
16576          * Event Object.
16577          *
16578          *
16579          */
16580         event: false, 
16581     /**
16582          * wrapper for event.on - aliased later..  
16583          * Typically use to register a event handler for register:
16584          *
16585          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16586          *
16587          */
16588     on : false
16589    
16590     
16591     
16592 });
16593
16594 Roo.XComponent.event = new Roo.util.Observable({
16595                 events : { 
16596                         /**
16597                          * @event register
16598                          * Fires when an Component is registered,
16599                          * set the disable property on the Component to stop registration.
16600                          * @param {Roo.XComponent} c the component being registerd.
16601                          * 
16602                          */
16603                         'register' : true,
16604             /**
16605                          * @event beforebuild
16606                          * Fires before each Component is built
16607                          * can be used to apply permissions.
16608                          * @param {Roo.XComponent} c the component being registerd.
16609                          * 
16610                          */
16611                         'beforebuild' : true,
16612                         /**
16613                          * @event buildcomplete
16614                          * Fires on the top level element when all elements have been built
16615                          * @param {Roo.XComponent} the top level component.
16616                          */
16617                         'buildcomplete' : true
16618                         
16619                 }
16620 });
16621
16622 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16623  //
16624  /**
16625  * marked - a markdown parser
16626  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16627  * https://github.com/chjj/marked
16628  */
16629
16630
16631 /**
16632  *
16633  * Roo.Markdown - is a very crude wrapper around marked..
16634  *
16635  * usage:
16636  * 
16637  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16638  * 
16639  * Note: move the sample code to the bottom of this
16640  * file before uncommenting it.
16641  *
16642  */
16643
16644 Roo.Markdown = {};
16645 Roo.Markdown.toHtml = function(text) {
16646     
16647     var c = new Roo.Markdown.marked.setOptions({
16648             renderer: new Roo.Markdown.marked.Renderer(),
16649             gfm: true,
16650             tables: true,
16651             breaks: false,
16652             pedantic: false,
16653             sanitize: false,
16654             smartLists: true,
16655             smartypants: false
16656           });
16657     // A FEW HACKS!!?
16658     
16659     text = text.replace(/\\\n/g,' ');
16660     return Roo.Markdown.marked(text);
16661 };
16662 //
16663 // converter
16664 //
16665 // Wraps all "globals" so that the only thing
16666 // exposed is makeHtml().
16667 //
16668 (function() {
16669     
16670     /**
16671      * Block-Level Grammar
16672      */
16673     
16674     var block = {
16675       newline: /^\n+/,
16676       code: /^( {4}[^\n]+\n*)+/,
16677       fences: noop,
16678       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16679       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16680       nptable: noop,
16681       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16682       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16683       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16684       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16685       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16686       table: noop,
16687       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16688       text: /^[^\n]+/
16689     };
16690     
16691     block.bullet = /(?:[*+-]|\d+\.)/;
16692     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16693     block.item = replace(block.item, 'gm')
16694       (/bull/g, block.bullet)
16695       ();
16696     
16697     block.list = replace(block.list)
16698       (/bull/g, block.bullet)
16699       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16700       ('def', '\\n+(?=' + block.def.source + ')')
16701       ();
16702     
16703     block.blockquote = replace(block.blockquote)
16704       ('def', block.def)
16705       ();
16706     
16707     block._tag = '(?!(?:'
16708       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16709       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16710       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16711     
16712     block.html = replace(block.html)
16713       ('comment', /<!--[\s\S]*?-->/)
16714       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16715       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16716       (/tag/g, block._tag)
16717       ();
16718     
16719     block.paragraph = replace(block.paragraph)
16720       ('hr', block.hr)
16721       ('heading', block.heading)
16722       ('lheading', block.lheading)
16723       ('blockquote', block.blockquote)
16724       ('tag', '<' + block._tag)
16725       ('def', block.def)
16726       ();
16727     
16728     /**
16729      * Normal Block Grammar
16730      */
16731     
16732     block.normal = merge({}, block);
16733     
16734     /**
16735      * GFM Block Grammar
16736      */
16737     
16738     block.gfm = merge({}, block.normal, {
16739       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16740       paragraph: /^/,
16741       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16742     });
16743     
16744     block.gfm.paragraph = replace(block.paragraph)
16745       ('(?!', '(?!'
16746         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16747         + block.list.source.replace('\\1', '\\3') + '|')
16748       ();
16749     
16750     /**
16751      * GFM + Tables Block Grammar
16752      */
16753     
16754     block.tables = merge({}, block.gfm, {
16755       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16756       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16757     });
16758     
16759     /**
16760      * Block Lexer
16761      */
16762     
16763     function Lexer(options) {
16764       this.tokens = [];
16765       this.tokens.links = {};
16766       this.options = options || marked.defaults;
16767       this.rules = block.normal;
16768     
16769       if (this.options.gfm) {
16770         if (this.options.tables) {
16771           this.rules = block.tables;
16772         } else {
16773           this.rules = block.gfm;
16774         }
16775       }
16776     }
16777     
16778     /**
16779      * Expose Block Rules
16780      */
16781     
16782     Lexer.rules = block;
16783     
16784     /**
16785      * Static Lex Method
16786      */
16787     
16788     Lexer.lex = function(src, options) {
16789       var lexer = new Lexer(options);
16790       return lexer.lex(src);
16791     };
16792     
16793     /**
16794      * Preprocessing
16795      */
16796     
16797     Lexer.prototype.lex = function(src) {
16798       src = src
16799         .replace(/\r\n|\r/g, '\n')
16800         .replace(/\t/g, '    ')
16801         .replace(/\u00a0/g, ' ')
16802         .replace(/\u2424/g, '\n');
16803     
16804       return this.token(src, true);
16805     };
16806     
16807     /**
16808      * Lexing
16809      */
16810     
16811     Lexer.prototype.token = function(src, top, bq) {
16812       var src = src.replace(/^ +$/gm, '')
16813         , next
16814         , loose
16815         , cap
16816         , bull
16817         , b
16818         , item
16819         , space
16820         , i
16821         , l;
16822     
16823       while (src) {
16824         // newline
16825         if (cap = this.rules.newline.exec(src)) {
16826           src = src.substring(cap[0].length);
16827           if (cap[0].length > 1) {
16828             this.tokens.push({
16829               type: 'space'
16830             });
16831           }
16832         }
16833     
16834         // code
16835         if (cap = this.rules.code.exec(src)) {
16836           src = src.substring(cap[0].length);
16837           cap = cap[0].replace(/^ {4}/gm, '');
16838           this.tokens.push({
16839             type: 'code',
16840             text: !this.options.pedantic
16841               ? cap.replace(/\n+$/, '')
16842               : cap
16843           });
16844           continue;
16845         }
16846     
16847         // fences (gfm)
16848         if (cap = this.rules.fences.exec(src)) {
16849           src = src.substring(cap[0].length);
16850           this.tokens.push({
16851             type: 'code',
16852             lang: cap[2],
16853             text: cap[3] || ''
16854           });
16855           continue;
16856         }
16857     
16858         // heading
16859         if (cap = this.rules.heading.exec(src)) {
16860           src = src.substring(cap[0].length);
16861           this.tokens.push({
16862             type: 'heading',
16863             depth: cap[1].length,
16864             text: cap[2]
16865           });
16866           continue;
16867         }
16868     
16869         // table no leading pipe (gfm)
16870         if (top && (cap = this.rules.nptable.exec(src))) {
16871           src = src.substring(cap[0].length);
16872     
16873           item = {
16874             type: 'table',
16875             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16876             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16877             cells: cap[3].replace(/\n$/, '').split('\n')
16878           };
16879     
16880           for (i = 0; i < item.align.length; i++) {
16881             if (/^ *-+: *$/.test(item.align[i])) {
16882               item.align[i] = 'right';
16883             } else if (/^ *:-+: *$/.test(item.align[i])) {
16884               item.align[i] = 'center';
16885             } else if (/^ *:-+ *$/.test(item.align[i])) {
16886               item.align[i] = 'left';
16887             } else {
16888               item.align[i] = null;
16889             }
16890           }
16891     
16892           for (i = 0; i < item.cells.length; i++) {
16893             item.cells[i] = item.cells[i].split(/ *\| */);
16894           }
16895     
16896           this.tokens.push(item);
16897     
16898           continue;
16899         }
16900     
16901         // lheading
16902         if (cap = this.rules.lheading.exec(src)) {
16903           src = src.substring(cap[0].length);
16904           this.tokens.push({
16905             type: 'heading',
16906             depth: cap[2] === '=' ? 1 : 2,
16907             text: cap[1]
16908           });
16909           continue;
16910         }
16911     
16912         // hr
16913         if (cap = this.rules.hr.exec(src)) {
16914           src = src.substring(cap[0].length);
16915           this.tokens.push({
16916             type: 'hr'
16917           });
16918           continue;
16919         }
16920     
16921         // blockquote
16922         if (cap = this.rules.blockquote.exec(src)) {
16923           src = src.substring(cap[0].length);
16924     
16925           this.tokens.push({
16926             type: 'blockquote_start'
16927           });
16928     
16929           cap = cap[0].replace(/^ *> ?/gm, '');
16930     
16931           // Pass `top` to keep the current
16932           // "toplevel" state. This is exactly
16933           // how markdown.pl works.
16934           this.token(cap, top, true);
16935     
16936           this.tokens.push({
16937             type: 'blockquote_end'
16938           });
16939     
16940           continue;
16941         }
16942     
16943         // list
16944         if (cap = this.rules.list.exec(src)) {
16945           src = src.substring(cap[0].length);
16946           bull = cap[2];
16947     
16948           this.tokens.push({
16949             type: 'list_start',
16950             ordered: bull.length > 1
16951           });
16952     
16953           // Get each top-level item.
16954           cap = cap[0].match(this.rules.item);
16955     
16956           next = false;
16957           l = cap.length;
16958           i = 0;
16959     
16960           for (; i < l; i++) {
16961             item = cap[i];
16962     
16963             // Remove the list item's bullet
16964             // so it is seen as the next token.
16965             space = item.length;
16966             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16967     
16968             // Outdent whatever the
16969             // list item contains. Hacky.
16970             if (~item.indexOf('\n ')) {
16971               space -= item.length;
16972               item = !this.options.pedantic
16973                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16974                 : item.replace(/^ {1,4}/gm, '');
16975             }
16976     
16977             // Determine whether the next list item belongs here.
16978             // Backpedal if it does not belong in this list.
16979             if (this.options.smartLists && i !== l - 1) {
16980               b = block.bullet.exec(cap[i + 1])[0];
16981               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16982                 src = cap.slice(i + 1).join('\n') + src;
16983                 i = l - 1;
16984               }
16985             }
16986     
16987             // Determine whether item is loose or not.
16988             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16989             // for discount behavior.
16990             loose = next || /\n\n(?!\s*$)/.test(item);
16991             if (i !== l - 1) {
16992               next = item.charAt(item.length - 1) === '\n';
16993               if (!loose) { loose = next; }
16994             }
16995     
16996             this.tokens.push({
16997               type: loose
16998                 ? 'loose_item_start'
16999                 : 'list_item_start'
17000             });
17001     
17002             // Recurse.
17003             this.token(item, false, bq);
17004     
17005             this.tokens.push({
17006               type: 'list_item_end'
17007             });
17008           }
17009     
17010           this.tokens.push({
17011             type: 'list_end'
17012           });
17013     
17014           continue;
17015         }
17016     
17017         // html
17018         if (cap = this.rules.html.exec(src)) {
17019           src = src.substring(cap[0].length);
17020           this.tokens.push({
17021             type: this.options.sanitize
17022               ? 'paragraph'
17023               : 'html',
17024             pre: !this.options.sanitizer
17025               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17026             text: cap[0]
17027           });
17028           continue;
17029         }
17030     
17031         // def
17032         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17033           src = src.substring(cap[0].length);
17034           this.tokens.links[cap[1].toLowerCase()] = {
17035             href: cap[2],
17036             title: cap[3]
17037           };
17038           continue;
17039         }
17040     
17041         // table (gfm)
17042         if (top && (cap = this.rules.table.exec(src))) {
17043           src = src.substring(cap[0].length);
17044     
17045           item = {
17046             type: 'table',
17047             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17048             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17049             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17050           };
17051     
17052           for (i = 0; i < item.align.length; i++) {
17053             if (/^ *-+: *$/.test(item.align[i])) {
17054               item.align[i] = 'right';
17055             } else if (/^ *:-+: *$/.test(item.align[i])) {
17056               item.align[i] = 'center';
17057             } else if (/^ *:-+ *$/.test(item.align[i])) {
17058               item.align[i] = 'left';
17059             } else {
17060               item.align[i] = null;
17061             }
17062           }
17063     
17064           for (i = 0; i < item.cells.length; i++) {
17065             item.cells[i] = item.cells[i]
17066               .replace(/^ *\| *| *\| *$/g, '')
17067               .split(/ *\| */);
17068           }
17069     
17070           this.tokens.push(item);
17071     
17072           continue;
17073         }
17074     
17075         // top-level paragraph
17076         if (top && (cap = this.rules.paragraph.exec(src))) {
17077           src = src.substring(cap[0].length);
17078           this.tokens.push({
17079             type: 'paragraph',
17080             text: cap[1].charAt(cap[1].length - 1) === '\n'
17081               ? cap[1].slice(0, -1)
17082               : cap[1]
17083           });
17084           continue;
17085         }
17086     
17087         // text
17088         if (cap = this.rules.text.exec(src)) {
17089           // Top-level should never reach here.
17090           src = src.substring(cap[0].length);
17091           this.tokens.push({
17092             type: 'text',
17093             text: cap[0]
17094           });
17095           continue;
17096         }
17097     
17098         if (src) {
17099           throw new
17100             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17101         }
17102       }
17103     
17104       return this.tokens;
17105     };
17106     
17107     /**
17108      * Inline-Level Grammar
17109      */
17110     
17111     var inline = {
17112       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17113       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17114       url: noop,
17115       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17116       link: /^!?\[(inside)\]\(href\)/,
17117       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17118       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17119       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17120       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17121       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17122       br: /^ {2,}\n(?!\s*$)/,
17123       del: noop,
17124       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17125     };
17126     
17127     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17128     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17129     
17130     inline.link = replace(inline.link)
17131       ('inside', inline._inside)
17132       ('href', inline._href)
17133       ();
17134     
17135     inline.reflink = replace(inline.reflink)
17136       ('inside', inline._inside)
17137       ();
17138     
17139     /**
17140      * Normal Inline Grammar
17141      */
17142     
17143     inline.normal = merge({}, inline);
17144     
17145     /**
17146      * Pedantic Inline Grammar
17147      */
17148     
17149     inline.pedantic = merge({}, inline.normal, {
17150       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17151       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17152     });
17153     
17154     /**
17155      * GFM Inline Grammar
17156      */
17157     
17158     inline.gfm = merge({}, inline.normal, {
17159       escape: replace(inline.escape)('])', '~|])')(),
17160       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17161       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17162       text: replace(inline.text)
17163         (']|', '~]|')
17164         ('|', '|https?://|')
17165         ()
17166     });
17167     
17168     /**
17169      * GFM + Line Breaks Inline Grammar
17170      */
17171     
17172     inline.breaks = merge({}, inline.gfm, {
17173       br: replace(inline.br)('{2,}', '*')(),
17174       text: replace(inline.gfm.text)('{2,}', '*')()
17175     });
17176     
17177     /**
17178      * Inline Lexer & Compiler
17179      */
17180     
17181     function InlineLexer(links, options) {
17182       this.options = options || marked.defaults;
17183       this.links = links;
17184       this.rules = inline.normal;
17185       this.renderer = this.options.renderer || new Renderer;
17186       this.renderer.options = this.options;
17187     
17188       if (!this.links) {
17189         throw new
17190           Error('Tokens array requires a `links` property.');
17191       }
17192     
17193       if (this.options.gfm) {
17194         if (this.options.breaks) {
17195           this.rules = inline.breaks;
17196         } else {
17197           this.rules = inline.gfm;
17198         }
17199       } else if (this.options.pedantic) {
17200         this.rules = inline.pedantic;
17201       }
17202     }
17203     
17204     /**
17205      * Expose Inline Rules
17206      */
17207     
17208     InlineLexer.rules = inline;
17209     
17210     /**
17211      * Static Lexing/Compiling Method
17212      */
17213     
17214     InlineLexer.output = function(src, links, options) {
17215       var inline = new InlineLexer(links, options);
17216       return inline.output(src);
17217     };
17218     
17219     /**
17220      * Lexing/Compiling
17221      */
17222     
17223     InlineLexer.prototype.output = function(src) {
17224       var out = ''
17225         , link
17226         , text
17227         , href
17228         , cap;
17229     
17230       while (src) {
17231         // escape
17232         if (cap = this.rules.escape.exec(src)) {
17233           src = src.substring(cap[0].length);
17234           out += cap[1];
17235           continue;
17236         }
17237     
17238         // autolink
17239         if (cap = this.rules.autolink.exec(src)) {
17240           src = src.substring(cap[0].length);
17241           if (cap[2] === '@') {
17242             text = cap[1].charAt(6) === ':'
17243               ? this.mangle(cap[1].substring(7))
17244               : this.mangle(cap[1]);
17245             href = this.mangle('mailto:') + text;
17246           } else {
17247             text = escape(cap[1]);
17248             href = text;
17249           }
17250           out += this.renderer.link(href, null, text);
17251           continue;
17252         }
17253     
17254         // url (gfm)
17255         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17256           src = src.substring(cap[0].length);
17257           text = escape(cap[1]);
17258           href = text;
17259           out += this.renderer.link(href, null, text);
17260           continue;
17261         }
17262     
17263         // tag
17264         if (cap = this.rules.tag.exec(src)) {
17265           if (!this.inLink && /^<a /i.test(cap[0])) {
17266             this.inLink = true;
17267           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17268             this.inLink = false;
17269           }
17270           src = src.substring(cap[0].length);
17271           out += this.options.sanitize
17272             ? this.options.sanitizer
17273               ? this.options.sanitizer(cap[0])
17274               : escape(cap[0])
17275             : cap[0];
17276           continue;
17277         }
17278     
17279         // link
17280         if (cap = this.rules.link.exec(src)) {
17281           src = src.substring(cap[0].length);
17282           this.inLink = true;
17283           out += this.outputLink(cap, {
17284             href: cap[2],
17285             title: cap[3]
17286           });
17287           this.inLink = false;
17288           continue;
17289         }
17290     
17291         // reflink, nolink
17292         if ((cap = this.rules.reflink.exec(src))
17293             || (cap = this.rules.nolink.exec(src))) {
17294           src = src.substring(cap[0].length);
17295           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17296           link = this.links[link.toLowerCase()];
17297           if (!link || !link.href) {
17298             out += cap[0].charAt(0);
17299             src = cap[0].substring(1) + src;
17300             continue;
17301           }
17302           this.inLink = true;
17303           out += this.outputLink(cap, link);
17304           this.inLink = false;
17305           continue;
17306         }
17307     
17308         // strong
17309         if (cap = this.rules.strong.exec(src)) {
17310           src = src.substring(cap[0].length);
17311           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17312           continue;
17313         }
17314     
17315         // em
17316         if (cap = this.rules.em.exec(src)) {
17317           src = src.substring(cap[0].length);
17318           out += this.renderer.em(this.output(cap[2] || cap[1]));
17319           continue;
17320         }
17321     
17322         // code
17323         if (cap = this.rules.code.exec(src)) {
17324           src = src.substring(cap[0].length);
17325           out += this.renderer.codespan(escape(cap[2], true));
17326           continue;
17327         }
17328     
17329         // br
17330         if (cap = this.rules.br.exec(src)) {
17331           src = src.substring(cap[0].length);
17332           out += this.renderer.br();
17333           continue;
17334         }
17335     
17336         // del (gfm)
17337         if (cap = this.rules.del.exec(src)) {
17338           src = src.substring(cap[0].length);
17339           out += this.renderer.del(this.output(cap[1]));
17340           continue;
17341         }
17342     
17343         // text
17344         if (cap = this.rules.text.exec(src)) {
17345           src = src.substring(cap[0].length);
17346           out += this.renderer.text(escape(this.smartypants(cap[0])));
17347           continue;
17348         }
17349     
17350         if (src) {
17351           throw new
17352             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17353         }
17354       }
17355     
17356       return out;
17357     };
17358     
17359     /**
17360      * Compile Link
17361      */
17362     
17363     InlineLexer.prototype.outputLink = function(cap, link) {
17364       var href = escape(link.href)
17365         , title = link.title ? escape(link.title) : null;
17366     
17367       return cap[0].charAt(0) !== '!'
17368         ? this.renderer.link(href, title, this.output(cap[1]))
17369         : this.renderer.image(href, title, escape(cap[1]));
17370     };
17371     
17372     /**
17373      * Smartypants Transformations
17374      */
17375     
17376     InlineLexer.prototype.smartypants = function(text) {
17377       if (!this.options.smartypants)  { return text; }
17378       return text
17379         // em-dashes
17380         .replace(/---/g, '\u2014')
17381         // en-dashes
17382         .replace(/--/g, '\u2013')
17383         // opening singles
17384         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17385         // closing singles & apostrophes
17386         .replace(/'/g, '\u2019')
17387         // opening doubles
17388         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17389         // closing doubles
17390         .replace(/"/g, '\u201d')
17391         // ellipses
17392         .replace(/\.{3}/g, '\u2026');
17393     };
17394     
17395     /**
17396      * Mangle Links
17397      */
17398     
17399     InlineLexer.prototype.mangle = function(text) {
17400       if (!this.options.mangle) { return text; }
17401       var out = ''
17402         , l = text.length
17403         , i = 0
17404         , ch;
17405     
17406       for (; i < l; i++) {
17407         ch = text.charCodeAt(i);
17408         if (Math.random() > 0.5) {
17409           ch = 'x' + ch.toString(16);
17410         }
17411         out += '&#' + ch + ';';
17412       }
17413     
17414       return out;
17415     };
17416     
17417     /**
17418      * Renderer
17419      */
17420     
17421     function Renderer(options) {
17422       this.options = options || {};
17423     }
17424     
17425     Renderer.prototype.code = function(code, lang, escaped) {
17426       if (this.options.highlight) {
17427         var out = this.options.highlight(code, lang);
17428         if (out != null && out !== code) {
17429           escaped = true;
17430           code = out;
17431         }
17432       } else {
17433             // hack!!! - it's already escapeD?
17434             escaped = true;
17435       }
17436     
17437       if (!lang) {
17438         return '<pre><code>'
17439           + (escaped ? code : escape(code, true))
17440           + '\n</code></pre>';
17441       }
17442     
17443       return '<pre><code class="'
17444         + this.options.langPrefix
17445         + escape(lang, true)
17446         + '">'
17447         + (escaped ? code : escape(code, true))
17448         + '\n</code></pre>\n';
17449     };
17450     
17451     Renderer.prototype.blockquote = function(quote) {
17452       return '<blockquote>\n' + quote + '</blockquote>\n';
17453     };
17454     
17455     Renderer.prototype.html = function(html) {
17456       return html;
17457     };
17458     
17459     Renderer.prototype.heading = function(text, level, raw) {
17460       return '<h'
17461         + level
17462         + ' id="'
17463         + this.options.headerPrefix
17464         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17465         + '">'
17466         + text
17467         + '</h'
17468         + level
17469         + '>\n';
17470     };
17471     
17472     Renderer.prototype.hr = function() {
17473       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17474     };
17475     
17476     Renderer.prototype.list = function(body, ordered) {
17477       var type = ordered ? 'ol' : 'ul';
17478       return '<' + type + '>\n' + body + '</' + type + '>\n';
17479     };
17480     
17481     Renderer.prototype.listitem = function(text) {
17482       return '<li>' + text + '</li>\n';
17483     };
17484     
17485     Renderer.prototype.paragraph = function(text) {
17486       return '<p>' + text + '</p>\n';
17487     };
17488     
17489     Renderer.prototype.table = function(header, body) {
17490       return '<table class="table table-striped">\n'
17491         + '<thead>\n'
17492         + header
17493         + '</thead>\n'
17494         + '<tbody>\n'
17495         + body
17496         + '</tbody>\n'
17497         + '</table>\n';
17498     };
17499     
17500     Renderer.prototype.tablerow = function(content) {
17501       return '<tr>\n' + content + '</tr>\n';
17502     };
17503     
17504     Renderer.prototype.tablecell = function(content, flags) {
17505       var type = flags.header ? 'th' : 'td';
17506       var tag = flags.align
17507         ? '<' + type + ' style="text-align:' + flags.align + '">'
17508         : '<' + type + '>';
17509       return tag + content + '</' + type + '>\n';
17510     };
17511     
17512     // span level renderer
17513     Renderer.prototype.strong = function(text) {
17514       return '<strong>' + text + '</strong>';
17515     };
17516     
17517     Renderer.prototype.em = function(text) {
17518       return '<em>' + text + '</em>';
17519     };
17520     
17521     Renderer.prototype.codespan = function(text) {
17522       return '<code>' + text + '</code>';
17523     };
17524     
17525     Renderer.prototype.br = function() {
17526       return this.options.xhtml ? '<br/>' : '<br>';
17527     };
17528     
17529     Renderer.prototype.del = function(text) {
17530       return '<del>' + text + '</del>';
17531     };
17532     
17533     Renderer.prototype.link = function(href, title, text) {
17534       if (this.options.sanitize) {
17535         try {
17536           var prot = decodeURIComponent(unescape(href))
17537             .replace(/[^\w:]/g, '')
17538             .toLowerCase();
17539         } catch (e) {
17540           return '';
17541         }
17542         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17543           return '';
17544         }
17545       }
17546       var out = '<a href="' + href + '"';
17547       if (title) {
17548         out += ' title="' + title + '"';
17549       }
17550       out += '>' + text + '</a>';
17551       return out;
17552     };
17553     
17554     Renderer.prototype.image = function(href, title, text) {
17555       var out = '<img src="' + href + '" alt="' + text + '"';
17556       if (title) {
17557         out += ' title="' + title + '"';
17558       }
17559       out += this.options.xhtml ? '/>' : '>';
17560       return out;
17561     };
17562     
17563     Renderer.prototype.text = function(text) {
17564       return text;
17565     };
17566     
17567     /**
17568      * Parsing & Compiling
17569      */
17570     
17571     function Parser(options) {
17572       this.tokens = [];
17573       this.token = null;
17574       this.options = options || marked.defaults;
17575       this.options.renderer = this.options.renderer || new Renderer;
17576       this.renderer = this.options.renderer;
17577       this.renderer.options = this.options;
17578     }
17579     
17580     /**
17581      * Static Parse Method
17582      */
17583     
17584     Parser.parse = function(src, options, renderer) {
17585       var parser = new Parser(options, renderer);
17586       return parser.parse(src);
17587     };
17588     
17589     /**
17590      * Parse Loop
17591      */
17592     
17593     Parser.prototype.parse = function(src) {
17594       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17595       this.tokens = src.reverse();
17596     
17597       var out = '';
17598       while (this.next()) {
17599         out += this.tok();
17600       }
17601     
17602       return out;
17603     };
17604     
17605     /**
17606      * Next Token
17607      */
17608     
17609     Parser.prototype.next = function() {
17610       return this.token = this.tokens.pop();
17611     };
17612     
17613     /**
17614      * Preview Next Token
17615      */
17616     
17617     Parser.prototype.peek = function() {
17618       return this.tokens[this.tokens.length - 1] || 0;
17619     };
17620     
17621     /**
17622      * Parse Text Tokens
17623      */
17624     
17625     Parser.prototype.parseText = function() {
17626       var body = this.token.text;
17627     
17628       while (this.peek().type === 'text') {
17629         body += '\n' + this.next().text;
17630       }
17631     
17632       return this.inline.output(body);
17633     };
17634     
17635     /**
17636      * Parse Current Token
17637      */
17638     
17639     Parser.prototype.tok = function() {
17640       switch (this.token.type) {
17641         case 'space': {
17642           return '';
17643         }
17644         case 'hr': {
17645           return this.renderer.hr();
17646         }
17647         case 'heading': {
17648           return this.renderer.heading(
17649             this.inline.output(this.token.text),
17650             this.token.depth,
17651             this.token.text);
17652         }
17653         case 'code': {
17654           return this.renderer.code(this.token.text,
17655             this.token.lang,
17656             this.token.escaped);
17657         }
17658         case 'table': {
17659           var header = ''
17660             , body = ''
17661             , i
17662             , row
17663             , cell
17664             , flags
17665             , j;
17666     
17667           // header
17668           cell = '';
17669           for (i = 0; i < this.token.header.length; i++) {
17670             flags = { header: true, align: this.token.align[i] };
17671             cell += this.renderer.tablecell(
17672               this.inline.output(this.token.header[i]),
17673               { header: true, align: this.token.align[i] }
17674             );
17675           }
17676           header += this.renderer.tablerow(cell);
17677     
17678           for (i = 0; i < this.token.cells.length; i++) {
17679             row = this.token.cells[i];
17680     
17681             cell = '';
17682             for (j = 0; j < row.length; j++) {
17683               cell += this.renderer.tablecell(
17684                 this.inline.output(row[j]),
17685                 { header: false, align: this.token.align[j] }
17686               );
17687             }
17688     
17689             body += this.renderer.tablerow(cell);
17690           }
17691           return this.renderer.table(header, body);
17692         }
17693         case 'blockquote_start': {
17694           var body = '';
17695     
17696           while (this.next().type !== 'blockquote_end') {
17697             body += this.tok();
17698           }
17699     
17700           return this.renderer.blockquote(body);
17701         }
17702         case 'list_start': {
17703           var body = ''
17704             , ordered = this.token.ordered;
17705     
17706           while (this.next().type !== 'list_end') {
17707             body += this.tok();
17708           }
17709     
17710           return this.renderer.list(body, ordered);
17711         }
17712         case 'list_item_start': {
17713           var body = '';
17714     
17715           while (this.next().type !== 'list_item_end') {
17716             body += this.token.type === 'text'
17717               ? this.parseText()
17718               : this.tok();
17719           }
17720     
17721           return this.renderer.listitem(body);
17722         }
17723         case 'loose_item_start': {
17724           var body = '';
17725     
17726           while (this.next().type !== 'list_item_end') {
17727             body += this.tok();
17728           }
17729     
17730           return this.renderer.listitem(body);
17731         }
17732         case 'html': {
17733           var html = !this.token.pre && !this.options.pedantic
17734             ? this.inline.output(this.token.text)
17735             : this.token.text;
17736           return this.renderer.html(html);
17737         }
17738         case 'paragraph': {
17739           return this.renderer.paragraph(this.inline.output(this.token.text));
17740         }
17741         case 'text': {
17742           return this.renderer.paragraph(this.parseText());
17743         }
17744       }
17745     };
17746     
17747     /**
17748      * Helpers
17749      */
17750     
17751     function escape(html, encode) {
17752       return html
17753         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17754         .replace(/</g, '&lt;')
17755         .replace(/>/g, '&gt;')
17756         .replace(/"/g, '&quot;')
17757         .replace(/'/g, '&#39;');
17758     }
17759     
17760     function unescape(html) {
17761         // explicitly match decimal, hex, and named HTML entities 
17762       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17763         n = n.toLowerCase();
17764         if (n === 'colon') { return ':'; }
17765         if (n.charAt(0) === '#') {
17766           return n.charAt(1) === 'x'
17767             ? String.fromCharCode(parseInt(n.substring(2), 16))
17768             : String.fromCharCode(+n.substring(1));
17769         }
17770         return '';
17771       });
17772     }
17773     
17774     function replace(regex, opt) {
17775       regex = regex.source;
17776       opt = opt || '';
17777       return function self(name, val) {
17778         if (!name) { return new RegExp(regex, opt); }
17779         val = val.source || val;
17780         val = val.replace(/(^|[^\[])\^/g, '$1');
17781         regex = regex.replace(name, val);
17782         return self;
17783       };
17784     }
17785     
17786     function noop() {}
17787     noop.exec = noop;
17788     
17789     function merge(obj) {
17790       var i = 1
17791         , target
17792         , key;
17793     
17794       for (; i < arguments.length; i++) {
17795         target = arguments[i];
17796         for (key in target) {
17797           if (Object.prototype.hasOwnProperty.call(target, key)) {
17798             obj[key] = target[key];
17799           }
17800         }
17801       }
17802     
17803       return obj;
17804     }
17805     
17806     
17807     /**
17808      * Marked
17809      */
17810     
17811     function marked(src, opt, callback) {
17812       if (callback || typeof opt === 'function') {
17813         if (!callback) {
17814           callback = opt;
17815           opt = null;
17816         }
17817     
17818         opt = merge({}, marked.defaults, opt || {});
17819     
17820         var highlight = opt.highlight
17821           , tokens
17822           , pending
17823           , i = 0;
17824     
17825         try {
17826           tokens = Lexer.lex(src, opt)
17827         } catch (e) {
17828           return callback(e);
17829         }
17830     
17831         pending = tokens.length;
17832     
17833         var done = function(err) {
17834           if (err) {
17835             opt.highlight = highlight;
17836             return callback(err);
17837           }
17838     
17839           var out;
17840     
17841           try {
17842             out = Parser.parse(tokens, opt);
17843           } catch (e) {
17844             err = e;
17845           }
17846     
17847           opt.highlight = highlight;
17848     
17849           return err
17850             ? callback(err)
17851             : callback(null, out);
17852         };
17853     
17854         if (!highlight || highlight.length < 3) {
17855           return done();
17856         }
17857     
17858         delete opt.highlight;
17859     
17860         if (!pending) { return done(); }
17861     
17862         for (; i < tokens.length; i++) {
17863           (function(token) {
17864             if (token.type !== 'code') {
17865               return --pending || done();
17866             }
17867             return highlight(token.text, token.lang, function(err, code) {
17868               if (err) { return done(err); }
17869               if (code == null || code === token.text) {
17870                 return --pending || done();
17871               }
17872               token.text = code;
17873               token.escaped = true;
17874               --pending || done();
17875             });
17876           })(tokens[i]);
17877         }
17878     
17879         return;
17880       }
17881       try {
17882         if (opt) { opt = merge({}, marked.defaults, opt); }
17883         return Parser.parse(Lexer.lex(src, opt), opt);
17884       } catch (e) {
17885         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17886         if ((opt || marked.defaults).silent) {
17887           return '<p>An error occured:</p><pre>'
17888             + escape(e.message + '', true)
17889             + '</pre>';
17890         }
17891         throw e;
17892       }
17893     }
17894     
17895     /**
17896      * Options
17897      */
17898     
17899     marked.options =
17900     marked.setOptions = function(opt) {
17901       merge(marked.defaults, opt);
17902       return marked;
17903     };
17904     
17905     marked.defaults = {
17906       gfm: true,
17907       tables: true,
17908       breaks: false,
17909       pedantic: false,
17910       sanitize: false,
17911       sanitizer: null,
17912       mangle: true,
17913       smartLists: false,
17914       silent: false,
17915       highlight: null,
17916       langPrefix: 'lang-',
17917       smartypants: false,
17918       headerPrefix: '',
17919       renderer: new Renderer,
17920       xhtml: false
17921     };
17922     
17923     /**
17924      * Expose
17925      */
17926     
17927     marked.Parser = Parser;
17928     marked.parser = Parser.parse;
17929     
17930     marked.Renderer = Renderer;
17931     
17932     marked.Lexer = Lexer;
17933     marked.lexer = Lexer.lex;
17934     
17935     marked.InlineLexer = InlineLexer;
17936     marked.inlineLexer = InlineLexer.output;
17937     
17938     marked.parse = marked;
17939     
17940     Roo.Markdown.marked = marked;
17941
17942 })();/*
17943  * Based on:
17944  * Ext JS Library 1.1.1
17945  * Copyright(c) 2006-2007, Ext JS, LLC.
17946  *
17947  * Originally Released Under LGPL - original licence link has changed is not relivant.
17948  *
17949  * Fork - LGPL
17950  * <script type="text/javascript">
17951  */
17952
17953
17954
17955 /*
17956  * These classes are derivatives of the similarly named classes in the YUI Library.
17957  * The original license:
17958  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17959  * Code licensed under the BSD License:
17960  * http://developer.yahoo.net/yui/license.txt
17961  */
17962
17963 (function() {
17964
17965 var Event=Roo.EventManager;
17966 var Dom=Roo.lib.Dom;
17967
17968 /**
17969  * @class Roo.dd.DragDrop
17970  * @extends Roo.util.Observable
17971  * Defines the interface and base operation of items that that can be
17972  * dragged or can be drop targets.  It was designed to be extended, overriding
17973  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17974  * Up to three html elements can be associated with a DragDrop instance:
17975  * <ul>
17976  * <li>linked element: the element that is passed into the constructor.
17977  * This is the element which defines the boundaries for interaction with
17978  * other DragDrop objects.</li>
17979  * <li>handle element(s): The drag operation only occurs if the element that
17980  * was clicked matches a handle element.  By default this is the linked
17981  * element, but there are times that you will want only a portion of the
17982  * linked element to initiate the drag operation, and the setHandleElId()
17983  * method provides a way to define this.</li>
17984  * <li>drag element: this represents the element that would be moved along
17985  * with the cursor during a drag operation.  By default, this is the linked
17986  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17987  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17988  * </li>
17989  * </ul>
17990  * This class should not be instantiated until the onload event to ensure that
17991  * the associated elements are available.
17992  * The following would define a DragDrop obj that would interact with any
17993  * other DragDrop obj in the "group1" group:
17994  * <pre>
17995  *  dd = new Roo.dd.DragDrop("div1", "group1");
17996  * </pre>
17997  * Since none of the event handlers have been implemented, nothing would
17998  * actually happen if you were to run the code above.  Normally you would
17999  * override this class or one of the default implementations, but you can
18000  * also override the methods you want on an instance of the class...
18001  * <pre>
18002  *  dd.onDragDrop = function(e, id) {
18003  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18004  *  }
18005  * </pre>
18006  * @constructor
18007  * @param {String} id of the element that is linked to this instance
18008  * @param {String} sGroup the group of related DragDrop objects
18009  * @param {object} config an object containing configurable attributes
18010  *                Valid properties for DragDrop:
18011  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18012  */
18013 Roo.dd.DragDrop = function(id, sGroup, config) {
18014     if (id) {
18015         this.init(id, sGroup, config);
18016     }
18017     
18018 };
18019
18020 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18021
18022     /**
18023      * The id of the element associated with this object.  This is what we
18024      * refer to as the "linked element" because the size and position of
18025      * this element is used to determine when the drag and drop objects have
18026      * interacted.
18027      * @property id
18028      * @type String
18029      */
18030     id: null,
18031
18032     /**
18033      * Configuration attributes passed into the constructor
18034      * @property config
18035      * @type object
18036      */
18037     config: null,
18038
18039     /**
18040      * The id of the element that will be dragged.  By default this is same
18041      * as the linked element , but could be changed to another element. Ex:
18042      * Roo.dd.DDProxy
18043      * @property dragElId
18044      * @type String
18045      * @private
18046      */
18047     dragElId: null,
18048
18049     /**
18050      * the id of the element that initiates the drag operation.  By default
18051      * this is the linked element, but could be changed to be a child of this
18052      * element.  This lets us do things like only starting the drag when the
18053      * header element within the linked html element is clicked.
18054      * @property handleElId
18055      * @type String
18056      * @private
18057      */
18058     handleElId: null,
18059
18060     /**
18061      * An associative array of HTML tags that will be ignored if clicked.
18062      * @property invalidHandleTypes
18063      * @type {string: string}
18064      */
18065     invalidHandleTypes: null,
18066
18067     /**
18068      * An associative array of ids for elements that will be ignored if clicked
18069      * @property invalidHandleIds
18070      * @type {string: string}
18071      */
18072     invalidHandleIds: null,
18073
18074     /**
18075      * An indexted array of css class names for elements that will be ignored
18076      * if clicked.
18077      * @property invalidHandleClasses
18078      * @type string[]
18079      */
18080     invalidHandleClasses: null,
18081
18082     /**
18083      * The linked element's absolute X position at the time the drag was
18084      * started
18085      * @property startPageX
18086      * @type int
18087      * @private
18088      */
18089     startPageX: 0,
18090
18091     /**
18092      * The linked element's absolute X position at the time the drag was
18093      * started
18094      * @property startPageY
18095      * @type int
18096      * @private
18097      */
18098     startPageY: 0,
18099
18100     /**
18101      * The group defines a logical collection of DragDrop objects that are
18102      * related.  Instances only get events when interacting with other
18103      * DragDrop object in the same group.  This lets us define multiple
18104      * groups using a single DragDrop subclass if we want.
18105      * @property groups
18106      * @type {string: string}
18107      */
18108     groups: null,
18109
18110     /**
18111      * Individual drag/drop instances can be locked.  This will prevent
18112      * onmousedown start drag.
18113      * @property locked
18114      * @type boolean
18115      * @private
18116      */
18117     locked: false,
18118
18119     /**
18120      * Lock this instance
18121      * @method lock
18122      */
18123     lock: function() { this.locked = true; },
18124
18125     /**
18126      * Unlock this instace
18127      * @method unlock
18128      */
18129     unlock: function() { this.locked = false; },
18130
18131     /**
18132      * By default, all insances can be a drop target.  This can be disabled by
18133      * setting isTarget to false.
18134      * @method isTarget
18135      * @type boolean
18136      */
18137     isTarget: true,
18138
18139     /**
18140      * The padding configured for this drag and drop object for calculating
18141      * the drop zone intersection with this object.
18142      * @method padding
18143      * @type int[]
18144      */
18145     padding: null,
18146
18147     /**
18148      * Cached reference to the linked element
18149      * @property _domRef
18150      * @private
18151      */
18152     _domRef: null,
18153
18154     /**
18155      * Internal typeof flag
18156      * @property __ygDragDrop
18157      * @private
18158      */
18159     __ygDragDrop: true,
18160
18161     /**
18162      * Set to true when horizontal contraints are applied
18163      * @property constrainX
18164      * @type boolean
18165      * @private
18166      */
18167     constrainX: false,
18168
18169     /**
18170      * Set to true when vertical contraints are applied
18171      * @property constrainY
18172      * @type boolean
18173      * @private
18174      */
18175     constrainY: false,
18176
18177     /**
18178      * The left constraint
18179      * @property minX
18180      * @type int
18181      * @private
18182      */
18183     minX: 0,
18184
18185     /**
18186      * The right constraint
18187      * @property maxX
18188      * @type int
18189      * @private
18190      */
18191     maxX: 0,
18192
18193     /**
18194      * The up constraint
18195      * @property minY
18196      * @type int
18197      * @type int
18198      * @private
18199      */
18200     minY: 0,
18201
18202     /**
18203      * The down constraint
18204      * @property maxY
18205      * @type int
18206      * @private
18207      */
18208     maxY: 0,
18209
18210     /**
18211      * Maintain offsets when we resetconstraints.  Set to true when you want
18212      * the position of the element relative to its parent to stay the same
18213      * when the page changes
18214      *
18215      * @property maintainOffset
18216      * @type boolean
18217      */
18218     maintainOffset: false,
18219
18220     /**
18221      * Array of pixel locations the element will snap to if we specified a
18222      * horizontal graduation/interval.  This array is generated automatically
18223      * when you define a tick interval.
18224      * @property xTicks
18225      * @type int[]
18226      */
18227     xTicks: null,
18228
18229     /**
18230      * Array of pixel locations the element will snap to if we specified a
18231      * vertical graduation/interval.  This array is generated automatically
18232      * when you define a tick interval.
18233      * @property yTicks
18234      * @type int[]
18235      */
18236     yTicks: null,
18237
18238     /**
18239      * By default the drag and drop instance will only respond to the primary
18240      * button click (left button for a right-handed mouse).  Set to true to
18241      * allow drag and drop to start with any mouse click that is propogated
18242      * by the browser
18243      * @property primaryButtonOnly
18244      * @type boolean
18245      */
18246     primaryButtonOnly: true,
18247
18248     /**
18249      * The availabe property is false until the linked dom element is accessible.
18250      * @property available
18251      * @type boolean
18252      */
18253     available: false,
18254
18255     /**
18256      * By default, drags can only be initiated if the mousedown occurs in the
18257      * region the linked element is.  This is done in part to work around a
18258      * bug in some browsers that mis-report the mousedown if the previous
18259      * mouseup happened outside of the window.  This property is set to true
18260      * if outer handles are defined.
18261      *
18262      * @property hasOuterHandles
18263      * @type boolean
18264      * @default false
18265      */
18266     hasOuterHandles: false,
18267
18268     /**
18269      * Code that executes immediately before the startDrag event
18270      * @method b4StartDrag
18271      * @private
18272      */
18273     b4StartDrag: function(x, y) { },
18274
18275     /**
18276      * Abstract method called after a drag/drop object is clicked
18277      * and the drag or mousedown time thresholds have beeen met.
18278      * @method startDrag
18279      * @param {int} X click location
18280      * @param {int} Y click location
18281      */
18282     startDrag: function(x, y) { /* override this */ },
18283
18284     /**
18285      * Code that executes immediately before the onDrag event
18286      * @method b4Drag
18287      * @private
18288      */
18289     b4Drag: function(e) { },
18290
18291     /**
18292      * Abstract method called during the onMouseMove event while dragging an
18293      * object.
18294      * @method onDrag
18295      * @param {Event} e the mousemove event
18296      */
18297     onDrag: function(e) { /* override this */ },
18298
18299     /**
18300      * Abstract method called when this element fist begins hovering over
18301      * another DragDrop obj
18302      * @method onDragEnter
18303      * @param {Event} e the mousemove event
18304      * @param {String|DragDrop[]} id In POINT mode, the element
18305      * id this is hovering over.  In INTERSECT mode, an array of one or more
18306      * dragdrop items being hovered over.
18307      */
18308     onDragEnter: function(e, id) { /* override this */ },
18309
18310     /**
18311      * Code that executes immediately before the onDragOver event
18312      * @method b4DragOver
18313      * @private
18314      */
18315     b4DragOver: function(e) { },
18316
18317     /**
18318      * Abstract method called when this element is hovering over another
18319      * DragDrop obj
18320      * @method onDragOver
18321      * @param {Event} e the mousemove event
18322      * @param {String|DragDrop[]} id In POINT mode, the element
18323      * id this is hovering over.  In INTERSECT mode, an array of dd items
18324      * being hovered over.
18325      */
18326     onDragOver: function(e, id) { /* override this */ },
18327
18328     /**
18329      * Code that executes immediately before the onDragOut event
18330      * @method b4DragOut
18331      * @private
18332      */
18333     b4DragOut: function(e) { },
18334
18335     /**
18336      * Abstract method called when we are no longer hovering over an element
18337      * @method onDragOut
18338      * @param {Event} e the mousemove event
18339      * @param {String|DragDrop[]} id In POINT mode, the element
18340      * id this was hovering over.  In INTERSECT mode, an array of dd items
18341      * that the mouse is no longer over.
18342      */
18343     onDragOut: function(e, id) { /* override this */ },
18344
18345     /**
18346      * Code that executes immediately before the onDragDrop event
18347      * @method b4DragDrop
18348      * @private
18349      */
18350     b4DragDrop: function(e) { },
18351
18352     /**
18353      * Abstract method called when this item is dropped on another DragDrop
18354      * obj
18355      * @method onDragDrop
18356      * @param {Event} e the mouseup event
18357      * @param {String|DragDrop[]} id In POINT mode, the element
18358      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18359      * was dropped on.
18360      */
18361     onDragDrop: function(e, id) { /* override this */ },
18362
18363     /**
18364      * Abstract method called when this item is dropped on an area with no
18365      * drop target
18366      * @method onInvalidDrop
18367      * @param {Event} e the mouseup event
18368      */
18369     onInvalidDrop: function(e) { /* override this */ },
18370
18371     /**
18372      * Code that executes immediately before the endDrag event
18373      * @method b4EndDrag
18374      * @private
18375      */
18376     b4EndDrag: function(e) { },
18377
18378     /**
18379      * Fired when we are done dragging the object
18380      * @method endDrag
18381      * @param {Event} e the mouseup event
18382      */
18383     endDrag: function(e) { /* override this */ },
18384
18385     /**
18386      * Code executed immediately before the onMouseDown event
18387      * @method b4MouseDown
18388      * @param {Event} e the mousedown event
18389      * @private
18390      */
18391     b4MouseDown: function(e) {  },
18392
18393     /**
18394      * Event handler that fires when a drag/drop obj gets a mousedown
18395      * @method onMouseDown
18396      * @param {Event} e the mousedown event
18397      */
18398     onMouseDown: function(e) { /* override this */ },
18399
18400     /**
18401      * Event handler that fires when a drag/drop obj gets a mouseup
18402      * @method onMouseUp
18403      * @param {Event} e the mouseup event
18404      */
18405     onMouseUp: function(e) { /* override this */ },
18406
18407     /**
18408      * Override the onAvailable method to do what is needed after the initial
18409      * position was determined.
18410      * @method onAvailable
18411      */
18412     onAvailable: function () {
18413     },
18414
18415     /*
18416      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18417      * @type Object
18418      */
18419     defaultPadding : {left:0, right:0, top:0, bottom:0},
18420
18421     /*
18422      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18423  *
18424  * Usage:
18425  <pre><code>
18426  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18427                 { dragElId: "existingProxyDiv" });
18428  dd.startDrag = function(){
18429      this.constrainTo("parent-id");
18430  };
18431  </code></pre>
18432  * Or you can initalize it using the {@link Roo.Element} object:
18433  <pre><code>
18434  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18435      startDrag : function(){
18436          this.constrainTo("parent-id");
18437      }
18438  });
18439  </code></pre>
18440      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18441      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18442      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18443      * an object containing the sides to pad. For example: {right:10, bottom:10}
18444      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18445      */
18446     constrainTo : function(constrainTo, pad, inContent){
18447         if(typeof pad == "number"){
18448             pad = {left: pad, right:pad, top:pad, bottom:pad};
18449         }
18450         pad = pad || this.defaultPadding;
18451         var b = Roo.get(this.getEl()).getBox();
18452         var ce = Roo.get(constrainTo);
18453         var s = ce.getScroll();
18454         var c, cd = ce.dom;
18455         if(cd == document.body){
18456             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18457         }else{
18458             xy = ce.getXY();
18459             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18460         }
18461
18462
18463         var topSpace = b.y - c.y;
18464         var leftSpace = b.x - c.x;
18465
18466         this.resetConstraints();
18467         this.setXConstraint(leftSpace - (pad.left||0), // left
18468                 c.width - leftSpace - b.width - (pad.right||0) //right
18469         );
18470         this.setYConstraint(topSpace - (pad.top||0), //top
18471                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18472         );
18473     },
18474
18475     /**
18476      * Returns a reference to the linked element
18477      * @method getEl
18478      * @return {HTMLElement} the html element
18479      */
18480     getEl: function() {
18481         if (!this._domRef) {
18482             this._domRef = Roo.getDom(this.id);
18483         }
18484
18485         return this._domRef;
18486     },
18487
18488     /**
18489      * Returns a reference to the actual element to drag.  By default this is
18490      * the same as the html element, but it can be assigned to another
18491      * element. An example of this can be found in Roo.dd.DDProxy
18492      * @method getDragEl
18493      * @return {HTMLElement} the html element
18494      */
18495     getDragEl: function() {
18496         return Roo.getDom(this.dragElId);
18497     },
18498
18499     /**
18500      * Sets up the DragDrop object.  Must be called in the constructor of any
18501      * Roo.dd.DragDrop subclass
18502      * @method init
18503      * @param id the id of the linked element
18504      * @param {String} sGroup the group of related items
18505      * @param {object} config configuration attributes
18506      */
18507     init: function(id, sGroup, config) {
18508         this.initTarget(id, sGroup, config);
18509         if (!Roo.isTouch) {
18510             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18511         }
18512         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18513         // Event.on(this.id, "selectstart", Event.preventDefault);
18514     },
18515
18516     /**
18517      * Initializes Targeting functionality only... the object does not
18518      * get a mousedown handler.
18519      * @method initTarget
18520      * @param id the id of the linked element
18521      * @param {String} sGroup the group of related items
18522      * @param {object} config configuration attributes
18523      */
18524     initTarget: function(id, sGroup, config) {
18525
18526         // configuration attributes
18527         this.config = config || {};
18528
18529         // create a local reference to the drag and drop manager
18530         this.DDM = Roo.dd.DDM;
18531         // initialize the groups array
18532         this.groups = {};
18533
18534         // assume that we have an element reference instead of an id if the
18535         // parameter is not a string
18536         if (typeof id !== "string") {
18537             id = Roo.id(id);
18538         }
18539
18540         // set the id
18541         this.id = id;
18542
18543         // add to an interaction group
18544         this.addToGroup((sGroup) ? sGroup : "default");
18545
18546         // We don't want to register this as the handle with the manager
18547         // so we just set the id rather than calling the setter.
18548         this.handleElId = id;
18549
18550         // the linked element is the element that gets dragged by default
18551         this.setDragElId(id);
18552
18553         // by default, clicked anchors will not start drag operations.
18554         this.invalidHandleTypes = { A: "A" };
18555         this.invalidHandleIds = {};
18556         this.invalidHandleClasses = [];
18557
18558         this.applyConfig();
18559
18560         this.handleOnAvailable();
18561     },
18562
18563     /**
18564      * Applies the configuration parameters that were passed into the constructor.
18565      * This is supposed to happen at each level through the inheritance chain.  So
18566      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18567      * DragDrop in order to get all of the parameters that are available in
18568      * each object.
18569      * @method applyConfig
18570      */
18571     applyConfig: function() {
18572
18573         // configurable properties:
18574         //    padding, isTarget, maintainOffset, primaryButtonOnly
18575         this.padding           = this.config.padding || [0, 0, 0, 0];
18576         this.isTarget          = (this.config.isTarget !== false);
18577         this.maintainOffset    = (this.config.maintainOffset);
18578         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18579
18580     },
18581
18582     /**
18583      * Executed when the linked element is available
18584      * @method handleOnAvailable
18585      * @private
18586      */
18587     handleOnAvailable: function() {
18588         this.available = true;
18589         this.resetConstraints();
18590         this.onAvailable();
18591     },
18592
18593      /**
18594      * Configures the padding for the target zone in px.  Effectively expands
18595      * (or reduces) the virtual object size for targeting calculations.
18596      * Supports css-style shorthand; if only one parameter is passed, all sides
18597      * will have that padding, and if only two are passed, the top and bottom
18598      * will have the first param, the left and right the second.
18599      * @method setPadding
18600      * @param {int} iTop    Top pad
18601      * @param {int} iRight  Right pad
18602      * @param {int} iBot    Bot pad
18603      * @param {int} iLeft   Left pad
18604      */
18605     setPadding: function(iTop, iRight, iBot, iLeft) {
18606         // this.padding = [iLeft, iRight, iTop, iBot];
18607         if (!iRight && 0 !== iRight) {
18608             this.padding = [iTop, iTop, iTop, iTop];
18609         } else if (!iBot && 0 !== iBot) {
18610             this.padding = [iTop, iRight, iTop, iRight];
18611         } else {
18612             this.padding = [iTop, iRight, iBot, iLeft];
18613         }
18614     },
18615
18616     /**
18617      * Stores the initial placement of the linked element.
18618      * @method setInitialPosition
18619      * @param {int} diffX   the X offset, default 0
18620      * @param {int} diffY   the Y offset, default 0
18621      */
18622     setInitPosition: function(diffX, diffY) {
18623         var el = this.getEl();
18624
18625         if (!this.DDM.verifyEl(el)) {
18626             return;
18627         }
18628
18629         var dx = diffX || 0;
18630         var dy = diffY || 0;
18631
18632         var p = Dom.getXY( el );
18633
18634         this.initPageX = p[0] - dx;
18635         this.initPageY = p[1] - dy;
18636
18637         this.lastPageX = p[0];
18638         this.lastPageY = p[1];
18639
18640
18641         this.setStartPosition(p);
18642     },
18643
18644     /**
18645      * Sets the start position of the element.  This is set when the obj
18646      * is initialized, the reset when a drag is started.
18647      * @method setStartPosition
18648      * @param pos current position (from previous lookup)
18649      * @private
18650      */
18651     setStartPosition: function(pos) {
18652         var p = pos || Dom.getXY( this.getEl() );
18653         this.deltaSetXY = null;
18654
18655         this.startPageX = p[0];
18656         this.startPageY = p[1];
18657     },
18658
18659     /**
18660      * Add this instance to a group of related drag/drop objects.  All
18661      * instances belong to at least one group, and can belong to as many
18662      * groups as needed.
18663      * @method addToGroup
18664      * @param sGroup {string} the name of the group
18665      */
18666     addToGroup: function(sGroup) {
18667         this.groups[sGroup] = true;
18668         this.DDM.regDragDrop(this, sGroup);
18669     },
18670
18671     /**
18672      * Remove's this instance from the supplied interaction group
18673      * @method removeFromGroup
18674      * @param {string}  sGroup  The group to drop
18675      */
18676     removeFromGroup: function(sGroup) {
18677         if (this.groups[sGroup]) {
18678             delete this.groups[sGroup];
18679         }
18680
18681         this.DDM.removeDDFromGroup(this, sGroup);
18682     },
18683
18684     /**
18685      * Allows you to specify that an element other than the linked element
18686      * will be moved with the cursor during a drag
18687      * @method setDragElId
18688      * @param id {string} the id of the element that will be used to initiate the drag
18689      */
18690     setDragElId: function(id) {
18691         this.dragElId = id;
18692     },
18693
18694     /**
18695      * Allows you to specify a child of the linked element that should be
18696      * used to initiate the drag operation.  An example of this would be if
18697      * you have a content div with text and links.  Clicking anywhere in the
18698      * content area would normally start the drag operation.  Use this method
18699      * to specify that an element inside of the content div is the element
18700      * that starts the drag operation.
18701      * @method setHandleElId
18702      * @param id {string} the id of the element that will be used to
18703      * initiate the drag.
18704      */
18705     setHandleElId: function(id) {
18706         if (typeof id !== "string") {
18707             id = Roo.id(id);
18708         }
18709         this.handleElId = id;
18710         this.DDM.regHandle(this.id, id);
18711     },
18712
18713     /**
18714      * Allows you to set an element outside of the linked element as a drag
18715      * handle
18716      * @method setOuterHandleElId
18717      * @param id the id of the element that will be used to initiate the drag
18718      */
18719     setOuterHandleElId: function(id) {
18720         if (typeof id !== "string") {
18721             id = Roo.id(id);
18722         }
18723         Event.on(id, "mousedown",
18724                 this.handleMouseDown, this);
18725         this.setHandleElId(id);
18726
18727         this.hasOuterHandles = true;
18728     },
18729
18730     /**
18731      * Remove all drag and drop hooks for this element
18732      * @method unreg
18733      */
18734     unreg: function() {
18735         Event.un(this.id, "mousedown",
18736                 this.handleMouseDown);
18737         Event.un(this.id, "touchstart",
18738                 this.handleMouseDown);
18739         this._domRef = null;
18740         this.DDM._remove(this);
18741     },
18742
18743     destroy : function(){
18744         this.unreg();
18745     },
18746
18747     /**
18748      * Returns true if this instance is locked, or the drag drop mgr is locked
18749      * (meaning that all drag/drop is disabled on the page.)
18750      * @method isLocked
18751      * @return {boolean} true if this obj or all drag/drop is locked, else
18752      * false
18753      */
18754     isLocked: function() {
18755         return (this.DDM.isLocked() || this.locked);
18756     },
18757
18758     /**
18759      * Fired when this object is clicked
18760      * @method handleMouseDown
18761      * @param {Event} e
18762      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18763      * @private
18764      */
18765     handleMouseDown: function(e, oDD){
18766      
18767         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18768             //Roo.log('not touch/ button !=0');
18769             return;
18770         }
18771         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18772             return; // double touch..
18773         }
18774         
18775
18776         if (this.isLocked()) {
18777             //Roo.log('locked');
18778             return;
18779         }
18780
18781         this.DDM.refreshCache(this.groups);
18782 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18783         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18784         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18785             //Roo.log('no outer handes or not over target');
18786                 // do nothing.
18787         } else {
18788 //            Roo.log('check validator');
18789             if (this.clickValidator(e)) {
18790 //                Roo.log('validate success');
18791                 // set the initial element position
18792                 this.setStartPosition();
18793
18794
18795                 this.b4MouseDown(e);
18796                 this.onMouseDown(e);
18797
18798                 this.DDM.handleMouseDown(e, this);
18799
18800                 this.DDM.stopEvent(e);
18801             } else {
18802
18803
18804             }
18805         }
18806     },
18807
18808     clickValidator: function(e) {
18809         var target = e.getTarget();
18810         return ( this.isValidHandleChild(target) &&
18811                     (this.id == this.handleElId ||
18812                         this.DDM.handleWasClicked(target, this.id)) );
18813     },
18814
18815     /**
18816      * Allows you to specify a tag name that should not start a drag operation
18817      * when clicked.  This is designed to facilitate embedding links within a
18818      * drag handle that do something other than start the drag.
18819      * @method addInvalidHandleType
18820      * @param {string} tagName the type of element to exclude
18821      */
18822     addInvalidHandleType: function(tagName) {
18823         var type = tagName.toUpperCase();
18824         this.invalidHandleTypes[type] = type;
18825     },
18826
18827     /**
18828      * Lets you to specify an element id for a child of a drag handle
18829      * that should not initiate a drag
18830      * @method addInvalidHandleId
18831      * @param {string} id the element id of the element you wish to ignore
18832      */
18833     addInvalidHandleId: function(id) {
18834         if (typeof id !== "string") {
18835             id = Roo.id(id);
18836         }
18837         this.invalidHandleIds[id] = id;
18838     },
18839
18840     /**
18841      * Lets you specify a css class of elements that will not initiate a drag
18842      * @method addInvalidHandleClass
18843      * @param {string} cssClass the class of the elements you wish to ignore
18844      */
18845     addInvalidHandleClass: function(cssClass) {
18846         this.invalidHandleClasses.push(cssClass);
18847     },
18848
18849     /**
18850      * Unsets an excluded tag name set by addInvalidHandleType
18851      * @method removeInvalidHandleType
18852      * @param {string} tagName the type of element to unexclude
18853      */
18854     removeInvalidHandleType: function(tagName) {
18855         var type = tagName.toUpperCase();
18856         // this.invalidHandleTypes[type] = null;
18857         delete this.invalidHandleTypes[type];
18858     },
18859
18860     /**
18861      * Unsets an invalid handle id
18862      * @method removeInvalidHandleId
18863      * @param {string} id the id of the element to re-enable
18864      */
18865     removeInvalidHandleId: function(id) {
18866         if (typeof id !== "string") {
18867             id = Roo.id(id);
18868         }
18869         delete this.invalidHandleIds[id];
18870     },
18871
18872     /**
18873      * Unsets an invalid css class
18874      * @method removeInvalidHandleClass
18875      * @param {string} cssClass the class of the element(s) you wish to
18876      * re-enable
18877      */
18878     removeInvalidHandleClass: function(cssClass) {
18879         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18880             if (this.invalidHandleClasses[i] == cssClass) {
18881                 delete this.invalidHandleClasses[i];
18882             }
18883         }
18884     },
18885
18886     /**
18887      * Checks the tag exclusion list to see if this click should be ignored
18888      * @method isValidHandleChild
18889      * @param {HTMLElement} node the HTMLElement to evaluate
18890      * @return {boolean} true if this is a valid tag type, false if not
18891      */
18892     isValidHandleChild: function(node) {
18893
18894         var valid = true;
18895         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18896         var nodeName;
18897         try {
18898             nodeName = node.nodeName.toUpperCase();
18899         } catch(e) {
18900             nodeName = node.nodeName;
18901         }
18902         valid = valid && !this.invalidHandleTypes[nodeName];
18903         valid = valid && !this.invalidHandleIds[node.id];
18904
18905         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18906             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18907         }
18908
18909
18910         return valid;
18911
18912     },
18913
18914     /**
18915      * Create the array of horizontal tick marks if an interval was specified
18916      * in setXConstraint().
18917      * @method setXTicks
18918      * @private
18919      */
18920     setXTicks: function(iStartX, iTickSize) {
18921         this.xTicks = [];
18922         this.xTickSize = iTickSize;
18923
18924         var tickMap = {};
18925
18926         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18927             if (!tickMap[i]) {
18928                 this.xTicks[this.xTicks.length] = i;
18929                 tickMap[i] = true;
18930             }
18931         }
18932
18933         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18934             if (!tickMap[i]) {
18935                 this.xTicks[this.xTicks.length] = i;
18936                 tickMap[i] = true;
18937             }
18938         }
18939
18940         this.xTicks.sort(this.DDM.numericSort) ;
18941     },
18942
18943     /**
18944      * Create the array of vertical tick marks if an interval was specified in
18945      * setYConstraint().
18946      * @method setYTicks
18947      * @private
18948      */
18949     setYTicks: function(iStartY, iTickSize) {
18950         this.yTicks = [];
18951         this.yTickSize = iTickSize;
18952
18953         var tickMap = {};
18954
18955         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18956             if (!tickMap[i]) {
18957                 this.yTicks[this.yTicks.length] = i;
18958                 tickMap[i] = true;
18959             }
18960         }
18961
18962         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18963             if (!tickMap[i]) {
18964                 this.yTicks[this.yTicks.length] = i;
18965                 tickMap[i] = true;
18966             }
18967         }
18968
18969         this.yTicks.sort(this.DDM.numericSort) ;
18970     },
18971
18972     /**
18973      * By default, the element can be dragged any place on the screen.  Use
18974      * this method to limit the horizontal travel of the element.  Pass in
18975      * 0,0 for the parameters if you want to lock the drag to the y axis.
18976      * @method setXConstraint
18977      * @param {int} iLeft the number of pixels the element can move to the left
18978      * @param {int} iRight the number of pixels the element can move to the
18979      * right
18980      * @param {int} iTickSize optional parameter for specifying that the
18981      * element
18982      * should move iTickSize pixels at a time.
18983      */
18984     setXConstraint: function(iLeft, iRight, iTickSize) {
18985         this.leftConstraint = iLeft;
18986         this.rightConstraint = iRight;
18987
18988         this.minX = this.initPageX - iLeft;
18989         this.maxX = this.initPageX + iRight;
18990         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18991
18992         this.constrainX = true;
18993     },
18994
18995     /**
18996      * Clears any constraints applied to this instance.  Also clears ticks
18997      * since they can't exist independent of a constraint at this time.
18998      * @method clearConstraints
18999      */
19000     clearConstraints: function() {
19001         this.constrainX = false;
19002         this.constrainY = false;
19003         this.clearTicks();
19004     },
19005
19006     /**
19007      * Clears any tick interval defined for this instance
19008      * @method clearTicks
19009      */
19010     clearTicks: function() {
19011         this.xTicks = null;
19012         this.yTicks = null;
19013         this.xTickSize = 0;
19014         this.yTickSize = 0;
19015     },
19016
19017     /**
19018      * By default, the element can be dragged any place on the screen.  Set
19019      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19020      * parameters if you want to lock the drag to the x axis.
19021      * @method setYConstraint
19022      * @param {int} iUp the number of pixels the element can move up
19023      * @param {int} iDown the number of pixels the element can move down
19024      * @param {int} iTickSize optional parameter for specifying that the
19025      * element should move iTickSize pixels at a time.
19026      */
19027     setYConstraint: function(iUp, iDown, iTickSize) {
19028         this.topConstraint = iUp;
19029         this.bottomConstraint = iDown;
19030
19031         this.minY = this.initPageY - iUp;
19032         this.maxY = this.initPageY + iDown;
19033         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19034
19035         this.constrainY = true;
19036
19037     },
19038
19039     /**
19040      * resetConstraints must be called if you manually reposition a dd element.
19041      * @method resetConstraints
19042      * @param {boolean} maintainOffset
19043      */
19044     resetConstraints: function() {
19045
19046
19047         // Maintain offsets if necessary
19048         if (this.initPageX || this.initPageX === 0) {
19049             // figure out how much this thing has moved
19050             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19051             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19052
19053             this.setInitPosition(dx, dy);
19054
19055         // This is the first time we have detected the element's position
19056         } else {
19057             this.setInitPosition();
19058         }
19059
19060         if (this.constrainX) {
19061             this.setXConstraint( this.leftConstraint,
19062                                  this.rightConstraint,
19063                                  this.xTickSize        );
19064         }
19065
19066         if (this.constrainY) {
19067             this.setYConstraint( this.topConstraint,
19068                                  this.bottomConstraint,
19069                                  this.yTickSize         );
19070         }
19071     },
19072
19073     /**
19074      * Normally the drag element is moved pixel by pixel, but we can specify
19075      * that it move a number of pixels at a time.  This method resolves the
19076      * location when we have it set up like this.
19077      * @method getTick
19078      * @param {int} val where we want to place the object
19079      * @param {int[]} tickArray sorted array of valid points
19080      * @return {int} the closest tick
19081      * @private
19082      */
19083     getTick: function(val, tickArray) {
19084
19085         if (!tickArray) {
19086             // If tick interval is not defined, it is effectively 1 pixel,
19087             // so we return the value passed to us.
19088             return val;
19089         } else if (tickArray[0] >= val) {
19090             // The value is lower than the first tick, so we return the first
19091             // tick.
19092             return tickArray[0];
19093         } else {
19094             for (var i=0, len=tickArray.length; i<len; ++i) {
19095                 var next = i + 1;
19096                 if (tickArray[next] && tickArray[next] >= val) {
19097                     var diff1 = val - tickArray[i];
19098                     var diff2 = tickArray[next] - val;
19099                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19100                 }
19101             }
19102
19103             // The value is larger than the last tick, so we return the last
19104             // tick.
19105             return tickArray[tickArray.length - 1];
19106         }
19107     },
19108
19109     /**
19110      * toString method
19111      * @method toString
19112      * @return {string} string representation of the dd obj
19113      */
19114     toString: function() {
19115         return ("DragDrop " + this.id);
19116     }
19117
19118 });
19119
19120 })();
19121 /*
19122  * Based on:
19123  * Ext JS Library 1.1.1
19124  * Copyright(c) 2006-2007, Ext JS, LLC.
19125  *
19126  * Originally Released Under LGPL - original licence link has changed is not relivant.
19127  *
19128  * Fork - LGPL
19129  * <script type="text/javascript">
19130  */
19131
19132
19133 /**
19134  * The drag and drop utility provides a framework for building drag and drop
19135  * applications.  In addition to enabling drag and drop for specific elements,
19136  * the drag and drop elements are tracked by the manager class, and the
19137  * interactions between the various elements are tracked during the drag and
19138  * the implementing code is notified about these important moments.
19139  */
19140
19141 // Only load the library once.  Rewriting the manager class would orphan
19142 // existing drag and drop instances.
19143 if (!Roo.dd.DragDropMgr) {
19144
19145 /**
19146  * @class Roo.dd.DragDropMgr
19147  * DragDropMgr is a singleton that tracks the element interaction for
19148  * all DragDrop items in the window.  Generally, you will not call
19149  * this class directly, but it does have helper methods that could
19150  * be useful in your DragDrop implementations.
19151  * @singleton
19152  */
19153 Roo.dd.DragDropMgr = function() {
19154
19155     var Event = Roo.EventManager;
19156
19157     return {
19158
19159         /**
19160          * Two dimensional Array of registered DragDrop objects.  The first
19161          * dimension is the DragDrop item group, the second the DragDrop
19162          * object.
19163          * @property ids
19164          * @type {string: string}
19165          * @private
19166          * @static
19167          */
19168         ids: {},
19169
19170         /**
19171          * Array of element ids defined as drag handles.  Used to determine
19172          * if the element that generated the mousedown event is actually the
19173          * handle and not the html element itself.
19174          * @property handleIds
19175          * @type {string: string}
19176          * @private
19177          * @static
19178          */
19179         handleIds: {},
19180
19181         /**
19182          * the DragDrop object that is currently being dragged
19183          * @property dragCurrent
19184          * @type DragDrop
19185          * @private
19186          * @static
19187          **/
19188         dragCurrent: null,
19189
19190         /**
19191          * the DragDrop object(s) that are being hovered over
19192          * @property dragOvers
19193          * @type Array
19194          * @private
19195          * @static
19196          */
19197         dragOvers: {},
19198
19199         /**
19200          * the X distance between the cursor and the object being dragged
19201          * @property deltaX
19202          * @type int
19203          * @private
19204          * @static
19205          */
19206         deltaX: 0,
19207
19208         /**
19209          * the Y distance between the cursor and the object being dragged
19210          * @property deltaY
19211          * @type int
19212          * @private
19213          * @static
19214          */
19215         deltaY: 0,
19216
19217         /**
19218          * Flag to determine if we should prevent the default behavior of the
19219          * events we define. By default this is true, but this can be set to
19220          * false if you need the default behavior (not recommended)
19221          * @property preventDefault
19222          * @type boolean
19223          * @static
19224          */
19225         preventDefault: true,
19226
19227         /**
19228          * Flag to determine if we should stop the propagation of the events
19229          * we generate. This is true by default but you may want to set it to
19230          * false if the html element contains other features that require the
19231          * mouse click.
19232          * @property stopPropagation
19233          * @type boolean
19234          * @static
19235          */
19236         stopPropagation: true,
19237
19238         /**
19239          * Internal flag that is set to true when drag and drop has been
19240          * intialized
19241          * @property initialized
19242          * @private
19243          * @static
19244          */
19245         initalized: false,
19246
19247         /**
19248          * All drag and drop can be disabled.
19249          * @property locked
19250          * @private
19251          * @static
19252          */
19253         locked: false,
19254
19255         /**
19256          * Called the first time an element is registered.
19257          * @method init
19258          * @private
19259          * @static
19260          */
19261         init: function() {
19262             this.initialized = true;
19263         },
19264
19265         /**
19266          * In point mode, drag and drop interaction is defined by the
19267          * location of the cursor during the drag/drop
19268          * @property POINT
19269          * @type int
19270          * @static
19271          */
19272         POINT: 0,
19273
19274         /**
19275          * In intersect mode, drag and drop interactio nis defined by the
19276          * overlap of two or more drag and drop objects.
19277          * @property INTERSECT
19278          * @type int
19279          * @static
19280          */
19281         INTERSECT: 1,
19282
19283         /**
19284          * The current drag and drop mode.  Default: POINT
19285          * @property mode
19286          * @type int
19287          * @static
19288          */
19289         mode: 0,
19290
19291         /**
19292          * Runs method on all drag and drop objects
19293          * @method _execOnAll
19294          * @private
19295          * @static
19296          */
19297         _execOnAll: function(sMethod, args) {
19298             for (var i in this.ids) {
19299                 for (var j in this.ids[i]) {
19300                     var oDD = this.ids[i][j];
19301                     if (! this.isTypeOfDD(oDD)) {
19302                         continue;
19303                     }
19304                     oDD[sMethod].apply(oDD, args);
19305                 }
19306             }
19307         },
19308
19309         /**
19310          * Drag and drop initialization.  Sets up the global event handlers
19311          * @method _onLoad
19312          * @private
19313          * @static
19314          */
19315         _onLoad: function() {
19316
19317             this.init();
19318
19319             if (!Roo.isTouch) {
19320                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19321                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19322             }
19323             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19324             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19325             
19326             Event.on(window,   "unload",    this._onUnload, this, true);
19327             Event.on(window,   "resize",    this._onResize, this, true);
19328             // Event.on(window,   "mouseout",    this._test);
19329
19330         },
19331
19332         /**
19333          * Reset constraints on all drag and drop objs
19334          * @method _onResize
19335          * @private
19336          * @static
19337          */
19338         _onResize: function(e) {
19339             this._execOnAll("resetConstraints", []);
19340         },
19341
19342         /**
19343          * Lock all drag and drop functionality
19344          * @method lock
19345          * @static
19346          */
19347         lock: function() { this.locked = true; },
19348
19349         /**
19350          * Unlock all drag and drop functionality
19351          * @method unlock
19352          * @static
19353          */
19354         unlock: function() { this.locked = false; },
19355
19356         /**
19357          * Is drag and drop locked?
19358          * @method isLocked
19359          * @return {boolean} True if drag and drop is locked, false otherwise.
19360          * @static
19361          */
19362         isLocked: function() { return this.locked; },
19363
19364         /**
19365          * Location cache that is set for all drag drop objects when a drag is
19366          * initiated, cleared when the drag is finished.
19367          * @property locationCache
19368          * @private
19369          * @static
19370          */
19371         locationCache: {},
19372
19373         /**
19374          * Set useCache to false if you want to force object the lookup of each
19375          * drag and drop linked element constantly during a drag.
19376          * @property useCache
19377          * @type boolean
19378          * @static
19379          */
19380         useCache: true,
19381
19382         /**
19383          * The number of pixels that the mouse needs to move after the
19384          * mousedown before the drag is initiated.  Default=3;
19385          * @property clickPixelThresh
19386          * @type int
19387          * @static
19388          */
19389         clickPixelThresh: 3,
19390
19391         /**
19392          * The number of milliseconds after the mousedown event to initiate the
19393          * drag if we don't get a mouseup event. Default=1000
19394          * @property clickTimeThresh
19395          * @type int
19396          * @static
19397          */
19398         clickTimeThresh: 350,
19399
19400         /**
19401          * Flag that indicates that either the drag pixel threshold or the
19402          * mousdown time threshold has been met
19403          * @property dragThreshMet
19404          * @type boolean
19405          * @private
19406          * @static
19407          */
19408         dragThreshMet: false,
19409
19410         /**
19411          * Timeout used for the click time threshold
19412          * @property clickTimeout
19413          * @type Object
19414          * @private
19415          * @static
19416          */
19417         clickTimeout: null,
19418
19419         /**
19420          * The X position of the mousedown event stored for later use when a
19421          * drag threshold is met.
19422          * @property startX
19423          * @type int
19424          * @private
19425          * @static
19426          */
19427         startX: 0,
19428
19429         /**
19430          * The Y position of the mousedown event stored for later use when a
19431          * drag threshold is met.
19432          * @property startY
19433          * @type int
19434          * @private
19435          * @static
19436          */
19437         startY: 0,
19438
19439         /**
19440          * Each DragDrop instance must be registered with the DragDropMgr.
19441          * This is executed in DragDrop.init()
19442          * @method regDragDrop
19443          * @param {DragDrop} oDD the DragDrop object to register
19444          * @param {String} sGroup the name of the group this element belongs to
19445          * @static
19446          */
19447         regDragDrop: function(oDD, sGroup) {
19448             if (!this.initialized) { this.init(); }
19449
19450             if (!this.ids[sGroup]) {
19451                 this.ids[sGroup] = {};
19452             }
19453             this.ids[sGroup][oDD.id] = oDD;
19454         },
19455
19456         /**
19457          * Removes the supplied dd instance from the supplied group. Executed
19458          * by DragDrop.removeFromGroup, so don't call this function directly.
19459          * @method removeDDFromGroup
19460          * @private
19461          * @static
19462          */
19463         removeDDFromGroup: function(oDD, sGroup) {
19464             if (!this.ids[sGroup]) {
19465                 this.ids[sGroup] = {};
19466             }
19467
19468             var obj = this.ids[sGroup];
19469             if (obj && obj[oDD.id]) {
19470                 delete obj[oDD.id];
19471             }
19472         },
19473
19474         /**
19475          * Unregisters a drag and drop item.  This is executed in
19476          * DragDrop.unreg, use that method instead of calling this directly.
19477          * @method _remove
19478          * @private
19479          * @static
19480          */
19481         _remove: function(oDD) {
19482             for (var g in oDD.groups) {
19483                 if (g && this.ids[g][oDD.id]) {
19484                     delete this.ids[g][oDD.id];
19485                 }
19486             }
19487             delete this.handleIds[oDD.id];
19488         },
19489
19490         /**
19491          * Each DragDrop handle element must be registered.  This is done
19492          * automatically when executing DragDrop.setHandleElId()
19493          * @method regHandle
19494          * @param {String} sDDId the DragDrop id this element is a handle for
19495          * @param {String} sHandleId the id of the element that is the drag
19496          * handle
19497          * @static
19498          */
19499         regHandle: function(sDDId, sHandleId) {
19500             if (!this.handleIds[sDDId]) {
19501                 this.handleIds[sDDId] = {};
19502             }
19503             this.handleIds[sDDId][sHandleId] = sHandleId;
19504         },
19505
19506         /**
19507          * Utility function to determine if a given element has been
19508          * registered as a drag drop item.
19509          * @method isDragDrop
19510          * @param {String} id the element id to check
19511          * @return {boolean} true if this element is a DragDrop item,
19512          * false otherwise
19513          * @static
19514          */
19515         isDragDrop: function(id) {
19516             return ( this.getDDById(id) ) ? true : false;
19517         },
19518
19519         /**
19520          * Returns the drag and drop instances that are in all groups the
19521          * passed in instance belongs to.
19522          * @method getRelated
19523          * @param {DragDrop} p_oDD the obj to get related data for
19524          * @param {boolean} bTargetsOnly if true, only return targetable objs
19525          * @return {DragDrop[]} the related instances
19526          * @static
19527          */
19528         getRelated: function(p_oDD, bTargetsOnly) {
19529             var oDDs = [];
19530             for (var i in p_oDD.groups) {
19531                 for (j in this.ids[i]) {
19532                     var dd = this.ids[i][j];
19533                     if (! this.isTypeOfDD(dd)) {
19534                         continue;
19535                     }
19536                     if (!bTargetsOnly || dd.isTarget) {
19537                         oDDs[oDDs.length] = dd;
19538                     }
19539                 }
19540             }
19541
19542             return oDDs;
19543         },
19544
19545         /**
19546          * Returns true if the specified dd target is a legal target for
19547          * the specifice drag obj
19548          * @method isLegalTarget
19549          * @param {DragDrop} the drag obj
19550          * @param {DragDrop} the target
19551          * @return {boolean} true if the target is a legal target for the
19552          * dd obj
19553          * @static
19554          */
19555         isLegalTarget: function (oDD, oTargetDD) {
19556             var targets = this.getRelated(oDD, true);
19557             for (var i=0, len=targets.length;i<len;++i) {
19558                 if (targets[i].id == oTargetDD.id) {
19559                     return true;
19560                 }
19561             }
19562
19563             return false;
19564         },
19565
19566         /**
19567          * My goal is to be able to transparently determine if an object is
19568          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19569          * returns "object", oDD.constructor.toString() always returns
19570          * "DragDrop" and not the name of the subclass.  So for now it just
19571          * evaluates a well-known variable in DragDrop.
19572          * @method isTypeOfDD
19573          * @param {Object} the object to evaluate
19574          * @return {boolean} true if typeof oDD = DragDrop
19575          * @static
19576          */
19577         isTypeOfDD: function (oDD) {
19578             return (oDD && oDD.__ygDragDrop);
19579         },
19580
19581         /**
19582          * Utility function to determine if a given element has been
19583          * registered as a drag drop handle for the given Drag Drop object.
19584          * @method isHandle
19585          * @param {String} id the element id to check
19586          * @return {boolean} true if this element is a DragDrop handle, false
19587          * otherwise
19588          * @static
19589          */
19590         isHandle: function(sDDId, sHandleId) {
19591             return ( this.handleIds[sDDId] &&
19592                             this.handleIds[sDDId][sHandleId] );
19593         },
19594
19595         /**
19596          * Returns the DragDrop instance for a given id
19597          * @method getDDById
19598          * @param {String} id the id of the DragDrop object
19599          * @return {DragDrop} the drag drop object, null if it is not found
19600          * @static
19601          */
19602         getDDById: function(id) {
19603             for (var i in this.ids) {
19604                 if (this.ids[i][id]) {
19605                     return this.ids[i][id];
19606                 }
19607             }
19608             return null;
19609         },
19610
19611         /**
19612          * Fired after a registered DragDrop object gets the mousedown event.
19613          * Sets up the events required to track the object being dragged
19614          * @method handleMouseDown
19615          * @param {Event} e the event
19616          * @param oDD the DragDrop object being dragged
19617          * @private
19618          * @static
19619          */
19620         handleMouseDown: function(e, oDD) {
19621             if(Roo.QuickTips){
19622                 Roo.QuickTips.disable();
19623             }
19624             this.currentTarget = e.getTarget();
19625
19626             this.dragCurrent = oDD;
19627
19628             var el = oDD.getEl();
19629
19630             // track start position
19631             this.startX = e.getPageX();
19632             this.startY = e.getPageY();
19633
19634             this.deltaX = this.startX - el.offsetLeft;
19635             this.deltaY = this.startY - el.offsetTop;
19636
19637             this.dragThreshMet = false;
19638
19639             this.clickTimeout = setTimeout(
19640                     function() {
19641                         var DDM = Roo.dd.DDM;
19642                         DDM.startDrag(DDM.startX, DDM.startY);
19643                     },
19644                     this.clickTimeThresh );
19645         },
19646
19647         /**
19648          * Fired when either the drag pixel threshol or the mousedown hold
19649          * time threshold has been met.
19650          * @method startDrag
19651          * @param x {int} the X position of the original mousedown
19652          * @param y {int} the Y position of the original mousedown
19653          * @static
19654          */
19655         startDrag: function(x, y) {
19656             clearTimeout(this.clickTimeout);
19657             if (this.dragCurrent) {
19658                 this.dragCurrent.b4StartDrag(x, y);
19659                 this.dragCurrent.startDrag(x, y);
19660             }
19661             this.dragThreshMet = true;
19662         },
19663
19664         /**
19665          * Internal function to handle the mouseup event.  Will be invoked
19666          * from the context of the document.
19667          * @method handleMouseUp
19668          * @param {Event} e the event
19669          * @private
19670          * @static
19671          */
19672         handleMouseUp: function(e) {
19673
19674             if(Roo.QuickTips){
19675                 Roo.QuickTips.enable();
19676             }
19677             if (! this.dragCurrent) {
19678                 return;
19679             }
19680
19681             clearTimeout(this.clickTimeout);
19682
19683             if (this.dragThreshMet) {
19684                 this.fireEvents(e, true);
19685             } else {
19686             }
19687
19688             this.stopDrag(e);
19689
19690             this.stopEvent(e);
19691         },
19692
19693         /**
19694          * Utility to stop event propagation and event default, if these
19695          * features are turned on.
19696          * @method stopEvent
19697          * @param {Event} e the event as returned by this.getEvent()
19698          * @static
19699          */
19700         stopEvent: function(e){
19701             if(this.stopPropagation) {
19702                 e.stopPropagation();
19703             }
19704
19705             if (this.preventDefault) {
19706                 e.preventDefault();
19707             }
19708         },
19709
19710         /**
19711          * Internal function to clean up event handlers after the drag
19712          * operation is complete
19713          * @method stopDrag
19714          * @param {Event} e the event
19715          * @private
19716          * @static
19717          */
19718         stopDrag: function(e) {
19719             // Fire the drag end event for the item that was dragged
19720             if (this.dragCurrent) {
19721                 if (this.dragThreshMet) {
19722                     this.dragCurrent.b4EndDrag(e);
19723                     this.dragCurrent.endDrag(e);
19724                 }
19725
19726                 this.dragCurrent.onMouseUp(e);
19727             }
19728
19729             this.dragCurrent = null;
19730             this.dragOvers = {};
19731         },
19732
19733         /**
19734          * Internal function to handle the mousemove event.  Will be invoked
19735          * from the context of the html element.
19736          *
19737          * @TODO figure out what we can do about mouse events lost when the
19738          * user drags objects beyond the window boundary.  Currently we can
19739          * detect this in internet explorer by verifying that the mouse is
19740          * down during the mousemove event.  Firefox doesn't give us the
19741          * button state on the mousemove event.
19742          * @method handleMouseMove
19743          * @param {Event} e the event
19744          * @private
19745          * @static
19746          */
19747         handleMouseMove: function(e) {
19748             if (! this.dragCurrent) {
19749                 return true;
19750             }
19751
19752             // var button = e.which || e.button;
19753
19754             // check for IE mouseup outside of page boundary
19755             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19756                 this.stopEvent(e);
19757                 return this.handleMouseUp(e);
19758             }
19759
19760             if (!this.dragThreshMet) {
19761                 var diffX = Math.abs(this.startX - e.getPageX());
19762                 var diffY = Math.abs(this.startY - e.getPageY());
19763                 if (diffX > this.clickPixelThresh ||
19764                             diffY > this.clickPixelThresh) {
19765                     this.startDrag(this.startX, this.startY);
19766                 }
19767             }
19768
19769             if (this.dragThreshMet) {
19770                 this.dragCurrent.b4Drag(e);
19771                 this.dragCurrent.onDrag(e);
19772                 if(!this.dragCurrent.moveOnly){
19773                     this.fireEvents(e, false);
19774                 }
19775             }
19776
19777             this.stopEvent(e);
19778
19779             return true;
19780         },
19781
19782         /**
19783          * Iterates over all of the DragDrop elements to find ones we are
19784          * hovering over or dropping on
19785          * @method fireEvents
19786          * @param {Event} e the event
19787          * @param {boolean} isDrop is this a drop op or a mouseover op?
19788          * @private
19789          * @static
19790          */
19791         fireEvents: function(e, isDrop) {
19792             var dc = this.dragCurrent;
19793
19794             // If the user did the mouse up outside of the window, we could
19795             // get here even though we have ended the drag.
19796             if (!dc || dc.isLocked()) {
19797                 return;
19798             }
19799
19800             var pt = e.getPoint();
19801
19802             // cache the previous dragOver array
19803             var oldOvers = [];
19804
19805             var outEvts   = [];
19806             var overEvts  = [];
19807             var dropEvts  = [];
19808             var enterEvts = [];
19809
19810             // Check to see if the object(s) we were hovering over is no longer
19811             // being hovered over so we can fire the onDragOut event
19812             for (var i in this.dragOvers) {
19813
19814                 var ddo = this.dragOvers[i];
19815
19816                 if (! this.isTypeOfDD(ddo)) {
19817                     continue;
19818                 }
19819
19820                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19821                     outEvts.push( ddo );
19822                 }
19823
19824                 oldOvers[i] = true;
19825                 delete this.dragOvers[i];
19826             }
19827
19828             for (var sGroup in dc.groups) {
19829
19830                 if ("string" != typeof sGroup) {
19831                     continue;
19832                 }
19833
19834                 for (i in this.ids[sGroup]) {
19835                     var oDD = this.ids[sGroup][i];
19836                     if (! this.isTypeOfDD(oDD)) {
19837                         continue;
19838                     }
19839
19840                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19841                         if (this.isOverTarget(pt, oDD, this.mode)) {
19842                             // look for drop interactions
19843                             if (isDrop) {
19844                                 dropEvts.push( oDD );
19845                             // look for drag enter and drag over interactions
19846                             } else {
19847
19848                                 // initial drag over: dragEnter fires
19849                                 if (!oldOvers[oDD.id]) {
19850                                     enterEvts.push( oDD );
19851                                 // subsequent drag overs: dragOver fires
19852                                 } else {
19853                                     overEvts.push( oDD );
19854                                 }
19855
19856                                 this.dragOvers[oDD.id] = oDD;
19857                             }
19858                         }
19859                     }
19860                 }
19861             }
19862
19863             if (this.mode) {
19864                 if (outEvts.length) {
19865                     dc.b4DragOut(e, outEvts);
19866                     dc.onDragOut(e, outEvts);
19867                 }
19868
19869                 if (enterEvts.length) {
19870                     dc.onDragEnter(e, enterEvts);
19871                 }
19872
19873                 if (overEvts.length) {
19874                     dc.b4DragOver(e, overEvts);
19875                     dc.onDragOver(e, overEvts);
19876                 }
19877
19878                 if (dropEvts.length) {
19879                     dc.b4DragDrop(e, dropEvts);
19880                     dc.onDragDrop(e, dropEvts);
19881                 }
19882
19883             } else {
19884                 // fire dragout events
19885                 var len = 0;
19886                 for (i=0, len=outEvts.length; i<len; ++i) {
19887                     dc.b4DragOut(e, outEvts[i].id);
19888                     dc.onDragOut(e, outEvts[i].id);
19889                 }
19890
19891                 // fire enter events
19892                 for (i=0,len=enterEvts.length; i<len; ++i) {
19893                     // dc.b4DragEnter(e, oDD.id);
19894                     dc.onDragEnter(e, enterEvts[i].id);
19895                 }
19896
19897                 // fire over events
19898                 for (i=0,len=overEvts.length; i<len; ++i) {
19899                     dc.b4DragOver(e, overEvts[i].id);
19900                     dc.onDragOver(e, overEvts[i].id);
19901                 }
19902
19903                 // fire drop events
19904                 for (i=0, len=dropEvts.length; i<len; ++i) {
19905                     dc.b4DragDrop(e, dropEvts[i].id);
19906                     dc.onDragDrop(e, dropEvts[i].id);
19907                 }
19908
19909             }
19910
19911             // notify about a drop that did not find a target
19912             if (isDrop && !dropEvts.length) {
19913                 dc.onInvalidDrop(e);
19914             }
19915
19916         },
19917
19918         /**
19919          * Helper function for getting the best match from the list of drag
19920          * and drop objects returned by the drag and drop events when we are
19921          * in INTERSECT mode.  It returns either the first object that the
19922          * cursor is over, or the object that has the greatest overlap with
19923          * the dragged element.
19924          * @method getBestMatch
19925          * @param  {DragDrop[]} dds The array of drag and drop objects
19926          * targeted
19927          * @return {DragDrop}       The best single match
19928          * @static
19929          */
19930         getBestMatch: function(dds) {
19931             var winner = null;
19932             // Return null if the input is not what we expect
19933             //if (!dds || !dds.length || dds.length == 0) {
19934                // winner = null;
19935             // If there is only one item, it wins
19936             //} else if (dds.length == 1) {
19937
19938             var len = dds.length;
19939
19940             if (len == 1) {
19941                 winner = dds[0];
19942             } else {
19943                 // Loop through the targeted items
19944                 for (var i=0; i<len; ++i) {
19945                     var dd = dds[i];
19946                     // If the cursor is over the object, it wins.  If the
19947                     // cursor is over multiple matches, the first one we come
19948                     // to wins.
19949                     if (dd.cursorIsOver) {
19950                         winner = dd;
19951                         break;
19952                     // Otherwise the object with the most overlap wins
19953                     } else {
19954                         if (!winner ||
19955                             winner.overlap.getArea() < dd.overlap.getArea()) {
19956                             winner = dd;
19957                         }
19958                     }
19959                 }
19960             }
19961
19962             return winner;
19963         },
19964
19965         /**
19966          * Refreshes the cache of the top-left and bottom-right points of the
19967          * drag and drop objects in the specified group(s).  This is in the
19968          * format that is stored in the drag and drop instance, so typical
19969          * usage is:
19970          * <code>
19971          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19972          * </code>
19973          * Alternatively:
19974          * <code>
19975          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19976          * </code>
19977          * @TODO this really should be an indexed array.  Alternatively this
19978          * method could accept both.
19979          * @method refreshCache
19980          * @param {Object} groups an associative array of groups to refresh
19981          * @static
19982          */
19983         refreshCache: function(groups) {
19984             for (var sGroup in groups) {
19985                 if ("string" != typeof sGroup) {
19986                     continue;
19987                 }
19988                 for (var i in this.ids[sGroup]) {
19989                     var oDD = this.ids[sGroup][i];
19990
19991                     if (this.isTypeOfDD(oDD)) {
19992                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19993                         var loc = this.getLocation(oDD);
19994                         if (loc) {
19995                             this.locationCache[oDD.id] = loc;
19996                         } else {
19997                             delete this.locationCache[oDD.id];
19998                             // this will unregister the drag and drop object if
19999                             // the element is not in a usable state
20000                             // oDD.unreg();
20001                         }
20002                     }
20003                 }
20004             }
20005         },
20006
20007         /**
20008          * This checks to make sure an element exists and is in the DOM.  The
20009          * main purpose is to handle cases where innerHTML is used to remove
20010          * drag and drop objects from the DOM.  IE provides an 'unspecified
20011          * error' when trying to access the offsetParent of such an element
20012          * @method verifyEl
20013          * @param {HTMLElement} el the element to check
20014          * @return {boolean} true if the element looks usable
20015          * @static
20016          */
20017         verifyEl: function(el) {
20018             if (el) {
20019                 var parent;
20020                 if(Roo.isIE){
20021                     try{
20022                         parent = el.offsetParent;
20023                     }catch(e){}
20024                 }else{
20025                     parent = el.offsetParent;
20026                 }
20027                 if (parent) {
20028                     return true;
20029                 }
20030             }
20031
20032             return false;
20033         },
20034
20035         /**
20036          * Returns a Region object containing the drag and drop element's position
20037          * and size, including the padding configured for it
20038          * @method getLocation
20039          * @param {DragDrop} oDD the drag and drop object to get the
20040          *                       location for
20041          * @return {Roo.lib.Region} a Region object representing the total area
20042          *                             the element occupies, including any padding
20043          *                             the instance is configured for.
20044          * @static
20045          */
20046         getLocation: function(oDD) {
20047             if (! this.isTypeOfDD(oDD)) {
20048                 return null;
20049             }
20050
20051             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20052
20053             try {
20054                 pos= Roo.lib.Dom.getXY(el);
20055             } catch (e) { }
20056
20057             if (!pos) {
20058                 return null;
20059             }
20060
20061             x1 = pos[0];
20062             x2 = x1 + el.offsetWidth;
20063             y1 = pos[1];
20064             y2 = y1 + el.offsetHeight;
20065
20066             t = y1 - oDD.padding[0];
20067             r = x2 + oDD.padding[1];
20068             b = y2 + oDD.padding[2];
20069             l = x1 - oDD.padding[3];
20070
20071             return new Roo.lib.Region( t, r, b, l );
20072         },
20073
20074         /**
20075          * Checks the cursor location to see if it over the target
20076          * @method isOverTarget
20077          * @param {Roo.lib.Point} pt The point to evaluate
20078          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20079          * @return {boolean} true if the mouse is over the target
20080          * @private
20081          * @static
20082          */
20083         isOverTarget: function(pt, oTarget, intersect) {
20084             // use cache if available
20085             var loc = this.locationCache[oTarget.id];
20086             if (!loc || !this.useCache) {
20087                 loc = this.getLocation(oTarget);
20088                 this.locationCache[oTarget.id] = loc;
20089
20090             }
20091
20092             if (!loc) {
20093                 return false;
20094             }
20095
20096             oTarget.cursorIsOver = loc.contains( pt );
20097
20098             // DragDrop is using this as a sanity check for the initial mousedown
20099             // in this case we are done.  In POINT mode, if the drag obj has no
20100             // contraints, we are also done. Otherwise we need to evaluate the
20101             // location of the target as related to the actual location of the
20102             // dragged element.
20103             var dc = this.dragCurrent;
20104             if (!dc || !dc.getTargetCoord ||
20105                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20106                 return oTarget.cursorIsOver;
20107             }
20108
20109             oTarget.overlap = null;
20110
20111             // Get the current location of the drag element, this is the
20112             // location of the mouse event less the delta that represents
20113             // where the original mousedown happened on the element.  We
20114             // need to consider constraints and ticks as well.
20115             var pos = dc.getTargetCoord(pt.x, pt.y);
20116
20117             var el = dc.getDragEl();
20118             var curRegion = new Roo.lib.Region( pos.y,
20119                                                    pos.x + el.offsetWidth,
20120                                                    pos.y + el.offsetHeight,
20121                                                    pos.x );
20122
20123             var overlap = curRegion.intersect(loc);
20124
20125             if (overlap) {
20126                 oTarget.overlap = overlap;
20127                 return (intersect) ? true : oTarget.cursorIsOver;
20128             } else {
20129                 return false;
20130             }
20131         },
20132
20133         /**
20134          * unload event handler
20135          * @method _onUnload
20136          * @private
20137          * @static
20138          */
20139         _onUnload: function(e, me) {
20140             Roo.dd.DragDropMgr.unregAll();
20141         },
20142
20143         /**
20144          * Cleans up the drag and drop events and objects.
20145          * @method unregAll
20146          * @private
20147          * @static
20148          */
20149         unregAll: function() {
20150
20151             if (this.dragCurrent) {
20152                 this.stopDrag();
20153                 this.dragCurrent = null;
20154             }
20155
20156             this._execOnAll("unreg", []);
20157
20158             for (i in this.elementCache) {
20159                 delete this.elementCache[i];
20160             }
20161
20162             this.elementCache = {};
20163             this.ids = {};
20164         },
20165
20166         /**
20167          * A cache of DOM elements
20168          * @property elementCache
20169          * @private
20170          * @static
20171          */
20172         elementCache: {},
20173
20174         /**
20175          * Get the wrapper for the DOM element specified
20176          * @method getElWrapper
20177          * @param {String} id the id of the element to get
20178          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20179          * @private
20180          * @deprecated This wrapper isn't that useful
20181          * @static
20182          */
20183         getElWrapper: function(id) {
20184             var oWrapper = this.elementCache[id];
20185             if (!oWrapper || !oWrapper.el) {
20186                 oWrapper = this.elementCache[id] =
20187                     new this.ElementWrapper(Roo.getDom(id));
20188             }
20189             return oWrapper;
20190         },
20191
20192         /**
20193          * Returns the actual DOM element
20194          * @method getElement
20195          * @param {String} id the id of the elment to get
20196          * @return {Object} The element
20197          * @deprecated use Roo.getDom instead
20198          * @static
20199          */
20200         getElement: function(id) {
20201             return Roo.getDom(id);
20202         },
20203
20204         /**
20205          * Returns the style property for the DOM element (i.e.,
20206          * document.getElById(id).style)
20207          * @method getCss
20208          * @param {String} id the id of the elment to get
20209          * @return {Object} The style property of the element
20210          * @deprecated use Roo.getDom instead
20211          * @static
20212          */
20213         getCss: function(id) {
20214             var el = Roo.getDom(id);
20215             return (el) ? el.style : null;
20216         },
20217
20218         /**
20219          * Inner class for cached elements
20220          * @class DragDropMgr.ElementWrapper
20221          * @for DragDropMgr
20222          * @private
20223          * @deprecated
20224          */
20225         ElementWrapper: function(el) {
20226                 /**
20227                  * The element
20228                  * @property el
20229                  */
20230                 this.el = el || null;
20231                 /**
20232                  * The element id
20233                  * @property id
20234                  */
20235                 this.id = this.el && el.id;
20236                 /**
20237                  * A reference to the style property
20238                  * @property css
20239                  */
20240                 this.css = this.el && el.style;
20241             },
20242
20243         /**
20244          * Returns the X position of an html element
20245          * @method getPosX
20246          * @param el the element for which to get the position
20247          * @return {int} the X coordinate
20248          * @for DragDropMgr
20249          * @deprecated use Roo.lib.Dom.getX instead
20250          * @static
20251          */
20252         getPosX: function(el) {
20253             return Roo.lib.Dom.getX(el);
20254         },
20255
20256         /**
20257          * Returns the Y position of an html element
20258          * @method getPosY
20259          * @param el the element for which to get the position
20260          * @return {int} the Y coordinate
20261          * @deprecated use Roo.lib.Dom.getY instead
20262          * @static
20263          */
20264         getPosY: function(el) {
20265             return Roo.lib.Dom.getY(el);
20266         },
20267
20268         /**
20269          * Swap two nodes.  In IE, we use the native method, for others we
20270          * emulate the IE behavior
20271          * @method swapNode
20272          * @param n1 the first node to swap
20273          * @param n2 the other node to swap
20274          * @static
20275          */
20276         swapNode: function(n1, n2) {
20277             if (n1.swapNode) {
20278                 n1.swapNode(n2);
20279             } else {
20280                 var p = n2.parentNode;
20281                 var s = n2.nextSibling;
20282
20283                 if (s == n1) {
20284                     p.insertBefore(n1, n2);
20285                 } else if (n2 == n1.nextSibling) {
20286                     p.insertBefore(n2, n1);
20287                 } else {
20288                     n1.parentNode.replaceChild(n2, n1);
20289                     p.insertBefore(n1, s);
20290                 }
20291             }
20292         },
20293
20294         /**
20295          * Returns the current scroll position
20296          * @method getScroll
20297          * @private
20298          * @static
20299          */
20300         getScroll: function () {
20301             var t, l, dde=document.documentElement, db=document.body;
20302             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20303                 t = dde.scrollTop;
20304                 l = dde.scrollLeft;
20305             } else if (db) {
20306                 t = db.scrollTop;
20307                 l = db.scrollLeft;
20308             } else {
20309
20310             }
20311             return { top: t, left: l };
20312         },
20313
20314         /**
20315          * Returns the specified element style property
20316          * @method getStyle
20317          * @param {HTMLElement} el          the element
20318          * @param {string}      styleProp   the style property
20319          * @return {string} The value of the style property
20320          * @deprecated use Roo.lib.Dom.getStyle
20321          * @static
20322          */
20323         getStyle: function(el, styleProp) {
20324             return Roo.fly(el).getStyle(styleProp);
20325         },
20326
20327         /**
20328          * Gets the scrollTop
20329          * @method getScrollTop
20330          * @return {int} the document's scrollTop
20331          * @static
20332          */
20333         getScrollTop: function () { return this.getScroll().top; },
20334
20335         /**
20336          * Gets the scrollLeft
20337          * @method getScrollLeft
20338          * @return {int} the document's scrollTop
20339          * @static
20340          */
20341         getScrollLeft: function () { return this.getScroll().left; },
20342
20343         /**
20344          * Sets the x/y position of an element to the location of the
20345          * target element.
20346          * @method moveToEl
20347          * @param {HTMLElement} moveEl      The element to move
20348          * @param {HTMLElement} targetEl    The position reference element
20349          * @static
20350          */
20351         moveToEl: function (moveEl, targetEl) {
20352             var aCoord = Roo.lib.Dom.getXY(targetEl);
20353             Roo.lib.Dom.setXY(moveEl, aCoord);
20354         },
20355
20356         /**
20357          * Numeric array sort function
20358          * @method numericSort
20359          * @static
20360          */
20361         numericSort: function(a, b) { return (a - b); },
20362
20363         /**
20364          * Internal counter
20365          * @property _timeoutCount
20366          * @private
20367          * @static
20368          */
20369         _timeoutCount: 0,
20370
20371         /**
20372          * Trying to make the load order less important.  Without this we get
20373          * an error if this file is loaded before the Event Utility.
20374          * @method _addListeners
20375          * @private
20376          * @static
20377          */
20378         _addListeners: function() {
20379             var DDM = Roo.dd.DDM;
20380             if ( Roo.lib.Event && document ) {
20381                 DDM._onLoad();
20382             } else {
20383                 if (DDM._timeoutCount > 2000) {
20384                 } else {
20385                     setTimeout(DDM._addListeners, 10);
20386                     if (document && document.body) {
20387                         DDM._timeoutCount += 1;
20388                     }
20389                 }
20390             }
20391         },
20392
20393         /**
20394          * Recursively searches the immediate parent and all child nodes for
20395          * the handle element in order to determine wheter or not it was
20396          * clicked.
20397          * @method handleWasClicked
20398          * @param node the html element to inspect
20399          * @static
20400          */
20401         handleWasClicked: function(node, id) {
20402             if (this.isHandle(id, node.id)) {
20403                 return true;
20404             } else {
20405                 // check to see if this is a text node child of the one we want
20406                 var p = node.parentNode;
20407
20408                 while (p) {
20409                     if (this.isHandle(id, p.id)) {
20410                         return true;
20411                     } else {
20412                         p = p.parentNode;
20413                     }
20414                 }
20415             }
20416
20417             return false;
20418         }
20419
20420     };
20421
20422 }();
20423
20424 // shorter alias, save a few bytes
20425 Roo.dd.DDM = Roo.dd.DragDropMgr;
20426 Roo.dd.DDM._addListeners();
20427
20428 }/*
20429  * Based on:
20430  * Ext JS Library 1.1.1
20431  * Copyright(c) 2006-2007, Ext JS, LLC.
20432  *
20433  * Originally Released Under LGPL - original licence link has changed is not relivant.
20434  *
20435  * Fork - LGPL
20436  * <script type="text/javascript">
20437  */
20438
20439 /**
20440  * @class Roo.dd.DD
20441  * A DragDrop implementation where the linked element follows the
20442  * mouse cursor during a drag.
20443  * @extends Roo.dd.DragDrop
20444  * @constructor
20445  * @param {String} id the id of the linked element
20446  * @param {String} sGroup the group of related DragDrop items
20447  * @param {object} config an object containing configurable attributes
20448  *                Valid properties for DD:
20449  *                    scroll
20450  */
20451 Roo.dd.DD = function(id, sGroup, config) {
20452     if (id) {
20453         this.init(id, sGroup, config);
20454     }
20455 };
20456
20457 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20458
20459     /**
20460      * When set to true, the utility automatically tries to scroll the browser
20461      * window wehn a drag and drop element is dragged near the viewport boundary.
20462      * Defaults to true.
20463      * @property scroll
20464      * @type boolean
20465      */
20466     scroll: true,
20467
20468     /**
20469      * Sets the pointer offset to the distance between the linked element's top
20470      * left corner and the location the element was clicked
20471      * @method autoOffset
20472      * @param {int} iPageX the X coordinate of the click
20473      * @param {int} iPageY the Y coordinate of the click
20474      */
20475     autoOffset: function(iPageX, iPageY) {
20476         var x = iPageX - this.startPageX;
20477         var y = iPageY - this.startPageY;
20478         this.setDelta(x, y);
20479     },
20480
20481     /**
20482      * Sets the pointer offset.  You can call this directly to force the
20483      * offset to be in a particular location (e.g., pass in 0,0 to set it
20484      * to the center of the object)
20485      * @method setDelta
20486      * @param {int} iDeltaX the distance from the left
20487      * @param {int} iDeltaY the distance from the top
20488      */
20489     setDelta: function(iDeltaX, iDeltaY) {
20490         this.deltaX = iDeltaX;
20491         this.deltaY = iDeltaY;
20492     },
20493
20494     /**
20495      * Sets the drag element to the location of the mousedown or click event,
20496      * maintaining the cursor location relative to the location on the element
20497      * that was clicked.  Override this if you want to place the element in a
20498      * location other than where the cursor is.
20499      * @method setDragElPos
20500      * @param {int} iPageX the X coordinate of the mousedown or drag event
20501      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20502      */
20503     setDragElPos: function(iPageX, iPageY) {
20504         // the first time we do this, we are going to check to make sure
20505         // the element has css positioning
20506
20507         var el = this.getDragEl();
20508         this.alignElWithMouse(el, iPageX, iPageY);
20509     },
20510
20511     /**
20512      * Sets the element to the location of the mousedown or click event,
20513      * maintaining the cursor location relative to the location on the element
20514      * that was clicked.  Override this if you want to place the element in a
20515      * location other than where the cursor is.
20516      * @method alignElWithMouse
20517      * @param {HTMLElement} el the element to move
20518      * @param {int} iPageX the X coordinate of the mousedown or drag event
20519      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20520      */
20521     alignElWithMouse: function(el, iPageX, iPageY) {
20522         var oCoord = this.getTargetCoord(iPageX, iPageY);
20523         var fly = el.dom ? el : Roo.fly(el);
20524         if (!this.deltaSetXY) {
20525             var aCoord = [oCoord.x, oCoord.y];
20526             fly.setXY(aCoord);
20527             var newLeft = fly.getLeft(true);
20528             var newTop  = fly.getTop(true);
20529             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20530         } else {
20531             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20532         }
20533
20534         this.cachePosition(oCoord.x, oCoord.y);
20535         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20536         return oCoord;
20537     },
20538
20539     /**
20540      * Saves the most recent position so that we can reset the constraints and
20541      * tick marks on-demand.  We need to know this so that we can calculate the
20542      * number of pixels the element is offset from its original position.
20543      * @method cachePosition
20544      * @param iPageX the current x position (optional, this just makes it so we
20545      * don't have to look it up again)
20546      * @param iPageY the current y position (optional, this just makes it so we
20547      * don't have to look it up again)
20548      */
20549     cachePosition: function(iPageX, iPageY) {
20550         if (iPageX) {
20551             this.lastPageX = iPageX;
20552             this.lastPageY = iPageY;
20553         } else {
20554             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20555             this.lastPageX = aCoord[0];
20556             this.lastPageY = aCoord[1];
20557         }
20558     },
20559
20560     /**
20561      * Auto-scroll the window if the dragged object has been moved beyond the
20562      * visible window boundary.
20563      * @method autoScroll
20564      * @param {int} x the drag element's x position
20565      * @param {int} y the drag element's y position
20566      * @param {int} h the height of the drag element
20567      * @param {int} w the width of the drag element
20568      * @private
20569      */
20570     autoScroll: function(x, y, h, w) {
20571
20572         if (this.scroll) {
20573             // The client height
20574             var clientH = Roo.lib.Dom.getViewWidth();
20575
20576             // The client width
20577             var clientW = Roo.lib.Dom.getViewHeight();
20578
20579             // The amt scrolled down
20580             var st = this.DDM.getScrollTop();
20581
20582             // The amt scrolled right
20583             var sl = this.DDM.getScrollLeft();
20584
20585             // Location of the bottom of the element
20586             var bot = h + y;
20587
20588             // Location of the right of the element
20589             var right = w + x;
20590
20591             // The distance from the cursor to the bottom of the visible area,
20592             // adjusted so that we don't scroll if the cursor is beyond the
20593             // element drag constraints
20594             var toBot = (clientH + st - y - this.deltaY);
20595
20596             // The distance from the cursor to the right of the visible area
20597             var toRight = (clientW + sl - x - this.deltaX);
20598
20599
20600             // How close to the edge the cursor must be before we scroll
20601             // var thresh = (document.all) ? 100 : 40;
20602             var thresh = 40;
20603
20604             // How many pixels to scroll per autoscroll op.  This helps to reduce
20605             // clunky scrolling. IE is more sensitive about this ... it needs this
20606             // value to be higher.
20607             var scrAmt = (document.all) ? 80 : 30;
20608
20609             // Scroll down if we are near the bottom of the visible page and the
20610             // obj extends below the crease
20611             if ( bot > clientH && toBot < thresh ) {
20612                 window.scrollTo(sl, st + scrAmt);
20613             }
20614
20615             // Scroll up if the window is scrolled down and the top of the object
20616             // goes above the top border
20617             if ( y < st && st > 0 && y - st < thresh ) {
20618                 window.scrollTo(sl, st - scrAmt);
20619             }
20620
20621             // Scroll right if the obj is beyond the right border and the cursor is
20622             // near the border.
20623             if ( right > clientW && toRight < thresh ) {
20624                 window.scrollTo(sl + scrAmt, st);
20625             }
20626
20627             // Scroll left if the window has been scrolled to the right and the obj
20628             // extends past the left border
20629             if ( x < sl && sl > 0 && x - sl < thresh ) {
20630                 window.scrollTo(sl - scrAmt, st);
20631             }
20632         }
20633     },
20634
20635     /**
20636      * Finds the location the element should be placed if we want to move
20637      * it to where the mouse location less the click offset would place us.
20638      * @method getTargetCoord
20639      * @param {int} iPageX the X coordinate of the click
20640      * @param {int} iPageY the Y coordinate of the click
20641      * @return an object that contains the coordinates (Object.x and Object.y)
20642      * @private
20643      */
20644     getTargetCoord: function(iPageX, iPageY) {
20645
20646
20647         var x = iPageX - this.deltaX;
20648         var y = iPageY - this.deltaY;
20649
20650         if (this.constrainX) {
20651             if (x < this.minX) { x = this.minX; }
20652             if (x > this.maxX) { x = this.maxX; }
20653         }
20654
20655         if (this.constrainY) {
20656             if (y < this.minY) { y = this.minY; }
20657             if (y > this.maxY) { y = this.maxY; }
20658         }
20659
20660         x = this.getTick(x, this.xTicks);
20661         y = this.getTick(y, this.yTicks);
20662
20663
20664         return {x:x, y:y};
20665     },
20666
20667     /*
20668      * Sets up config options specific to this class. Overrides
20669      * Roo.dd.DragDrop, but all versions of this method through the
20670      * inheritance chain are called
20671      */
20672     applyConfig: function() {
20673         Roo.dd.DD.superclass.applyConfig.call(this);
20674         this.scroll = (this.config.scroll !== false);
20675     },
20676
20677     /*
20678      * Event that fires prior to the onMouseDown event.  Overrides
20679      * Roo.dd.DragDrop.
20680      */
20681     b4MouseDown: function(e) {
20682         // this.resetConstraints();
20683         this.autoOffset(e.getPageX(),
20684                             e.getPageY());
20685     },
20686
20687     /*
20688      * Event that fires prior to the onDrag event.  Overrides
20689      * Roo.dd.DragDrop.
20690      */
20691     b4Drag: function(e) {
20692         this.setDragElPos(e.getPageX(),
20693                             e.getPageY());
20694     },
20695
20696     toString: function() {
20697         return ("DD " + this.id);
20698     }
20699
20700     //////////////////////////////////////////////////////////////////////////
20701     // Debugging ygDragDrop events that can be overridden
20702     //////////////////////////////////////////////////////////////////////////
20703     /*
20704     startDrag: function(x, y) {
20705     },
20706
20707     onDrag: function(e) {
20708     },
20709
20710     onDragEnter: function(e, id) {
20711     },
20712
20713     onDragOver: function(e, id) {
20714     },
20715
20716     onDragOut: function(e, id) {
20717     },
20718
20719     onDragDrop: function(e, id) {
20720     },
20721
20722     endDrag: function(e) {
20723     }
20724
20725     */
20726
20727 });/*
20728  * Based on:
20729  * Ext JS Library 1.1.1
20730  * Copyright(c) 2006-2007, Ext JS, LLC.
20731  *
20732  * Originally Released Under LGPL - original licence link has changed is not relivant.
20733  *
20734  * Fork - LGPL
20735  * <script type="text/javascript">
20736  */
20737
20738 /**
20739  * @class Roo.dd.DDProxy
20740  * A DragDrop implementation that inserts an empty, bordered div into
20741  * the document that follows the cursor during drag operations.  At the time of
20742  * the click, the frame div is resized to the dimensions of the linked html
20743  * element, and moved to the exact location of the linked element.
20744  *
20745  * References to the "frame" element refer to the single proxy element that
20746  * was created to be dragged in place of all DDProxy elements on the
20747  * page.
20748  *
20749  * @extends Roo.dd.DD
20750  * @constructor
20751  * @param {String} id the id of the linked html element
20752  * @param {String} sGroup the group of related DragDrop objects
20753  * @param {object} config an object containing configurable attributes
20754  *                Valid properties for DDProxy in addition to those in DragDrop:
20755  *                   resizeFrame, centerFrame, dragElId
20756  */
20757 Roo.dd.DDProxy = function(id, sGroup, config) {
20758     if (id) {
20759         this.init(id, sGroup, config);
20760         this.initFrame();
20761     }
20762 };
20763
20764 /**
20765  * The default drag frame div id
20766  * @property Roo.dd.DDProxy.dragElId
20767  * @type String
20768  * @static
20769  */
20770 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20771
20772 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20773
20774     /**
20775      * By default we resize the drag frame to be the same size as the element
20776      * we want to drag (this is to get the frame effect).  We can turn it off
20777      * if we want a different behavior.
20778      * @property resizeFrame
20779      * @type boolean
20780      */
20781     resizeFrame: true,
20782
20783     /**
20784      * By default the frame is positioned exactly where the drag element is, so
20785      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20786      * you do not have constraints on the obj is to have the drag frame centered
20787      * around the cursor.  Set centerFrame to true for this effect.
20788      * @property centerFrame
20789      * @type boolean
20790      */
20791     centerFrame: false,
20792
20793     /**
20794      * Creates the proxy element if it does not yet exist
20795      * @method createFrame
20796      */
20797     createFrame: function() {
20798         var self = this;
20799         var body = document.body;
20800
20801         if (!body || !body.firstChild) {
20802             setTimeout( function() { self.createFrame(); }, 50 );
20803             return;
20804         }
20805
20806         var div = this.getDragEl();
20807
20808         if (!div) {
20809             div    = document.createElement("div");
20810             div.id = this.dragElId;
20811             var s  = div.style;
20812
20813             s.position   = "absolute";
20814             s.visibility = "hidden";
20815             s.cursor     = "move";
20816             s.border     = "2px solid #aaa";
20817             s.zIndex     = 999;
20818
20819             // appendChild can blow up IE if invoked prior to the window load event
20820             // while rendering a table.  It is possible there are other scenarios
20821             // that would cause this to happen as well.
20822             body.insertBefore(div, body.firstChild);
20823         }
20824     },
20825
20826     /**
20827      * Initialization for the drag frame element.  Must be called in the
20828      * constructor of all subclasses
20829      * @method initFrame
20830      */
20831     initFrame: function() {
20832         this.createFrame();
20833     },
20834
20835     applyConfig: function() {
20836         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20837
20838         this.resizeFrame = (this.config.resizeFrame !== false);
20839         this.centerFrame = (this.config.centerFrame);
20840         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20841     },
20842
20843     /**
20844      * Resizes the drag frame to the dimensions of the clicked object, positions
20845      * it over the object, and finally displays it
20846      * @method showFrame
20847      * @param {int} iPageX X click position
20848      * @param {int} iPageY Y click position
20849      * @private
20850      */
20851     showFrame: function(iPageX, iPageY) {
20852         var el = this.getEl();
20853         var dragEl = this.getDragEl();
20854         var s = dragEl.style;
20855
20856         this._resizeProxy();
20857
20858         if (this.centerFrame) {
20859             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20860                            Math.round(parseInt(s.height, 10)/2) );
20861         }
20862
20863         this.setDragElPos(iPageX, iPageY);
20864
20865         Roo.fly(dragEl).show();
20866     },
20867
20868     /**
20869      * The proxy is automatically resized to the dimensions of the linked
20870      * element when a drag is initiated, unless resizeFrame is set to false
20871      * @method _resizeProxy
20872      * @private
20873      */
20874     _resizeProxy: function() {
20875         if (this.resizeFrame) {
20876             var el = this.getEl();
20877             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20878         }
20879     },
20880
20881     // overrides Roo.dd.DragDrop
20882     b4MouseDown: function(e) {
20883         var x = e.getPageX();
20884         var y = e.getPageY();
20885         this.autoOffset(x, y);
20886         this.setDragElPos(x, y);
20887     },
20888
20889     // overrides Roo.dd.DragDrop
20890     b4StartDrag: function(x, y) {
20891         // show the drag frame
20892         this.showFrame(x, y);
20893     },
20894
20895     // overrides Roo.dd.DragDrop
20896     b4EndDrag: function(e) {
20897         Roo.fly(this.getDragEl()).hide();
20898     },
20899
20900     // overrides Roo.dd.DragDrop
20901     // By default we try to move the element to the last location of the frame.
20902     // This is so that the default behavior mirrors that of Roo.dd.DD.
20903     endDrag: function(e) {
20904
20905         var lel = this.getEl();
20906         var del = this.getDragEl();
20907
20908         // Show the drag frame briefly so we can get its position
20909         del.style.visibility = "";
20910
20911         this.beforeMove();
20912         // Hide the linked element before the move to get around a Safari
20913         // rendering bug.
20914         lel.style.visibility = "hidden";
20915         Roo.dd.DDM.moveToEl(lel, del);
20916         del.style.visibility = "hidden";
20917         lel.style.visibility = "";
20918
20919         this.afterDrag();
20920     },
20921
20922     beforeMove : function(){
20923
20924     },
20925
20926     afterDrag : function(){
20927
20928     },
20929
20930     toString: function() {
20931         return ("DDProxy " + this.id);
20932     }
20933
20934 });
20935 /*
20936  * Based on:
20937  * Ext JS Library 1.1.1
20938  * Copyright(c) 2006-2007, Ext JS, LLC.
20939  *
20940  * Originally Released Under LGPL - original licence link has changed is not relivant.
20941  *
20942  * Fork - LGPL
20943  * <script type="text/javascript">
20944  */
20945
20946  /**
20947  * @class Roo.dd.DDTarget
20948  * A DragDrop implementation that does not move, but can be a drop
20949  * target.  You would get the same result by simply omitting implementation
20950  * for the event callbacks, but this way we reduce the processing cost of the
20951  * event listener and the callbacks.
20952  * @extends Roo.dd.DragDrop
20953  * @constructor
20954  * @param {String} id the id of the element that is a drop target
20955  * @param {String} sGroup the group of related DragDrop objects
20956  * @param {object} config an object containing configurable attributes
20957  *                 Valid properties for DDTarget in addition to those in
20958  *                 DragDrop:
20959  *                    none
20960  */
20961 Roo.dd.DDTarget = function(id, sGroup, config) {
20962     if (id) {
20963         this.initTarget(id, sGroup, config);
20964     }
20965     if (config.listeners || config.events) { 
20966        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20967             listeners : config.listeners || {}, 
20968             events : config.events || {} 
20969         });    
20970     }
20971 };
20972
20973 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20974 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20975     toString: function() {
20976         return ("DDTarget " + this.id);
20977     }
20978 });
20979 /*
20980  * Based on:
20981  * Ext JS Library 1.1.1
20982  * Copyright(c) 2006-2007, Ext JS, LLC.
20983  *
20984  * Originally Released Under LGPL - original licence link has changed is not relivant.
20985  *
20986  * Fork - LGPL
20987  * <script type="text/javascript">
20988  */
20989  
20990
20991 /**
20992  * @class Roo.dd.ScrollManager
20993  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20994  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20995  * @singleton
20996  */
20997 Roo.dd.ScrollManager = function(){
20998     var ddm = Roo.dd.DragDropMgr;
20999     var els = {};
21000     var dragEl = null;
21001     var proc = {};
21002     
21003     
21004     
21005     var onStop = function(e){
21006         dragEl = null;
21007         clearProc();
21008     };
21009     
21010     var triggerRefresh = function(){
21011         if(ddm.dragCurrent){
21012              ddm.refreshCache(ddm.dragCurrent.groups);
21013         }
21014     };
21015     
21016     var doScroll = function(){
21017         if(ddm.dragCurrent){
21018             var dds = Roo.dd.ScrollManager;
21019             if(!dds.animate){
21020                 if(proc.el.scroll(proc.dir, dds.increment)){
21021                     triggerRefresh();
21022                 }
21023             }else{
21024                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21025             }
21026         }
21027     };
21028     
21029     var clearProc = function(){
21030         if(proc.id){
21031             clearInterval(proc.id);
21032         }
21033         proc.id = 0;
21034         proc.el = null;
21035         proc.dir = "";
21036     };
21037     
21038     var startProc = function(el, dir){
21039          Roo.log('scroll startproc');
21040         clearProc();
21041         proc.el = el;
21042         proc.dir = dir;
21043         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21044     };
21045     
21046     var onFire = function(e, isDrop){
21047        
21048         if(isDrop || !ddm.dragCurrent){ return; }
21049         var dds = Roo.dd.ScrollManager;
21050         if(!dragEl || dragEl != ddm.dragCurrent){
21051             dragEl = ddm.dragCurrent;
21052             // refresh regions on drag start
21053             dds.refreshCache();
21054         }
21055         
21056         var xy = Roo.lib.Event.getXY(e);
21057         var pt = new Roo.lib.Point(xy[0], xy[1]);
21058         for(var id in els){
21059             var el = els[id], r = el._region;
21060             if(r && r.contains(pt) && el.isScrollable()){
21061                 if(r.bottom - pt.y <= dds.thresh){
21062                     if(proc.el != el){
21063                         startProc(el, "down");
21064                     }
21065                     return;
21066                 }else if(r.right - pt.x <= dds.thresh){
21067                     if(proc.el != el){
21068                         startProc(el, "left");
21069                     }
21070                     return;
21071                 }else if(pt.y - r.top <= dds.thresh){
21072                     if(proc.el != el){
21073                         startProc(el, "up");
21074                     }
21075                     return;
21076                 }else if(pt.x - r.left <= dds.thresh){
21077                     if(proc.el != el){
21078                         startProc(el, "right");
21079                     }
21080                     return;
21081                 }
21082             }
21083         }
21084         clearProc();
21085     };
21086     
21087     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21088     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21089     
21090     return {
21091         /**
21092          * Registers new overflow element(s) to auto scroll
21093          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21094          */
21095         register : function(el){
21096             if(el instanceof Array){
21097                 for(var i = 0, len = el.length; i < len; i++) {
21098                         this.register(el[i]);
21099                 }
21100             }else{
21101                 el = Roo.get(el);
21102                 els[el.id] = el;
21103             }
21104             Roo.dd.ScrollManager.els = els;
21105         },
21106         
21107         /**
21108          * Unregisters overflow element(s) so they are no longer scrolled
21109          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21110          */
21111         unregister : function(el){
21112             if(el instanceof Array){
21113                 for(var i = 0, len = el.length; i < len; i++) {
21114                         this.unregister(el[i]);
21115                 }
21116             }else{
21117                 el = Roo.get(el);
21118                 delete els[el.id];
21119             }
21120         },
21121         
21122         /**
21123          * The number of pixels from the edge of a container the pointer needs to be to 
21124          * trigger scrolling (defaults to 25)
21125          * @type Number
21126          */
21127         thresh : 25,
21128         
21129         /**
21130          * The number of pixels to scroll in each scroll increment (defaults to 50)
21131          * @type Number
21132          */
21133         increment : 100,
21134         
21135         /**
21136          * The frequency of scrolls in milliseconds (defaults to 500)
21137          * @type Number
21138          */
21139         frequency : 500,
21140         
21141         /**
21142          * True to animate the scroll (defaults to true)
21143          * @type Boolean
21144          */
21145         animate: true,
21146         
21147         /**
21148          * The animation duration in seconds - 
21149          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21150          * @type Number
21151          */
21152         animDuration: .4,
21153         
21154         /**
21155          * Manually trigger a cache refresh.
21156          */
21157         refreshCache : function(){
21158             for(var id in els){
21159                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21160                     els[id]._region = els[id].getRegion();
21161                 }
21162             }
21163         }
21164     };
21165 }();/*
21166  * Based on:
21167  * Ext JS Library 1.1.1
21168  * Copyright(c) 2006-2007, Ext JS, LLC.
21169  *
21170  * Originally Released Under LGPL - original licence link has changed is not relivant.
21171  *
21172  * Fork - LGPL
21173  * <script type="text/javascript">
21174  */
21175  
21176
21177 /**
21178  * @class Roo.dd.Registry
21179  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21180  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21181  * @singleton
21182  */
21183 Roo.dd.Registry = function(){
21184     var elements = {}; 
21185     var handles = {}; 
21186     var autoIdSeed = 0;
21187
21188     var getId = function(el, autogen){
21189         if(typeof el == "string"){
21190             return el;
21191         }
21192         var id = el.id;
21193         if(!id && autogen !== false){
21194             id = "roodd-" + (++autoIdSeed);
21195             el.id = id;
21196         }
21197         return id;
21198     };
21199     
21200     return {
21201     /**
21202      * Register a drag drop element
21203      * @param {String|HTMLElement} element The id or DOM node to register
21204      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21205      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21206      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21207      * populated in the data object (if applicable):
21208      * <pre>
21209 Value      Description<br />
21210 ---------  ------------------------------------------<br />
21211 handles    Array of DOM nodes that trigger dragging<br />
21212            for the element being registered<br />
21213 isHandle   True if the element passed in triggers<br />
21214            dragging itself, else false
21215 </pre>
21216      */
21217         register : function(el, data){
21218             data = data || {};
21219             if(typeof el == "string"){
21220                 el = document.getElementById(el);
21221             }
21222             data.ddel = el;
21223             elements[getId(el)] = data;
21224             if(data.isHandle !== false){
21225                 handles[data.ddel.id] = data;
21226             }
21227             if(data.handles){
21228                 var hs = data.handles;
21229                 for(var i = 0, len = hs.length; i < len; i++){
21230                         handles[getId(hs[i])] = data;
21231                 }
21232             }
21233         },
21234
21235     /**
21236      * Unregister a drag drop element
21237      * @param {String|HTMLElement}  element The id or DOM node to unregister
21238      */
21239         unregister : function(el){
21240             var id = getId(el, false);
21241             var data = elements[id];
21242             if(data){
21243                 delete elements[id];
21244                 if(data.handles){
21245                     var hs = data.handles;
21246                     for(var i = 0, len = hs.length; i < len; i++){
21247                         delete handles[getId(hs[i], false)];
21248                     }
21249                 }
21250             }
21251         },
21252
21253     /**
21254      * Returns the handle registered for a DOM Node by id
21255      * @param {String|HTMLElement} id The DOM node or id to look up
21256      * @return {Object} handle The custom handle data
21257      */
21258         getHandle : function(id){
21259             if(typeof id != "string"){ // must be element?
21260                 id = id.id;
21261             }
21262             return handles[id];
21263         },
21264
21265     /**
21266      * Returns the handle that is registered for the DOM node that is the target of the event
21267      * @param {Event} e The event
21268      * @return {Object} handle The custom handle data
21269      */
21270         getHandleFromEvent : function(e){
21271             var t = Roo.lib.Event.getTarget(e);
21272             return t ? handles[t.id] : null;
21273         },
21274
21275     /**
21276      * Returns a custom data object that is registered for a DOM node by id
21277      * @param {String|HTMLElement} id The DOM node or id to look up
21278      * @return {Object} data The custom data
21279      */
21280         getTarget : function(id){
21281             if(typeof id != "string"){ // must be element?
21282                 id = id.id;
21283             }
21284             return elements[id];
21285         },
21286
21287     /**
21288      * Returns a custom data object that is registered for the DOM node that is the target of the event
21289      * @param {Event} e The event
21290      * @return {Object} data The custom data
21291      */
21292         getTargetFromEvent : function(e){
21293             var t = Roo.lib.Event.getTarget(e);
21294             return t ? elements[t.id] || handles[t.id] : null;
21295         }
21296     };
21297 }();/*
21298  * Based on:
21299  * Ext JS Library 1.1.1
21300  * Copyright(c) 2006-2007, Ext JS, LLC.
21301  *
21302  * Originally Released Under LGPL - original licence link has changed is not relivant.
21303  *
21304  * Fork - LGPL
21305  * <script type="text/javascript">
21306  */
21307  
21308
21309 /**
21310  * @class Roo.dd.StatusProxy
21311  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21312  * default drag proxy used by all Roo.dd components.
21313  * @constructor
21314  * @param {Object} config
21315  */
21316 Roo.dd.StatusProxy = function(config){
21317     Roo.apply(this, config);
21318     this.id = this.id || Roo.id();
21319     this.el = new Roo.Layer({
21320         dh: {
21321             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21322                 {tag: "div", cls: "x-dd-drop-icon"},
21323                 {tag: "div", cls: "x-dd-drag-ghost"}
21324             ]
21325         }, 
21326         shadow: !config || config.shadow !== false
21327     });
21328     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21329     this.dropStatus = this.dropNotAllowed;
21330 };
21331
21332 Roo.dd.StatusProxy.prototype = {
21333     /**
21334      * @cfg {String} dropAllowed
21335      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21336      */
21337     dropAllowed : "x-dd-drop-ok",
21338     /**
21339      * @cfg {String} dropNotAllowed
21340      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21341      */
21342     dropNotAllowed : "x-dd-drop-nodrop",
21343
21344     /**
21345      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21346      * over the current target element.
21347      * @param {String} cssClass The css class for the new drop status indicator image
21348      */
21349     setStatus : function(cssClass){
21350         cssClass = cssClass || this.dropNotAllowed;
21351         if(this.dropStatus != cssClass){
21352             this.el.replaceClass(this.dropStatus, cssClass);
21353             this.dropStatus = cssClass;
21354         }
21355     },
21356
21357     /**
21358      * Resets the status indicator to the default dropNotAllowed value
21359      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21360      */
21361     reset : function(clearGhost){
21362         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21363         this.dropStatus = this.dropNotAllowed;
21364         if(clearGhost){
21365             this.ghost.update("");
21366         }
21367     },
21368
21369     /**
21370      * Updates the contents of the ghost element
21371      * @param {String} html The html that will replace the current innerHTML of the ghost element
21372      */
21373     update : function(html){
21374         if(typeof html == "string"){
21375             this.ghost.update(html);
21376         }else{
21377             this.ghost.update("");
21378             html.style.margin = "0";
21379             this.ghost.dom.appendChild(html);
21380         }
21381         // ensure float = none set?? cant remember why though.
21382         var el = this.ghost.dom.firstChild;
21383                 if(el){
21384                         Roo.fly(el).setStyle('float', 'none');
21385                 }
21386     },
21387     
21388     /**
21389      * Returns the underlying proxy {@link Roo.Layer}
21390      * @return {Roo.Layer} el
21391     */
21392     getEl : function(){
21393         return this.el;
21394     },
21395
21396     /**
21397      * Returns the ghost element
21398      * @return {Roo.Element} el
21399      */
21400     getGhost : function(){
21401         return this.ghost;
21402     },
21403
21404     /**
21405      * Hides the proxy
21406      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21407      */
21408     hide : function(clear){
21409         this.el.hide();
21410         if(clear){
21411             this.reset(true);
21412         }
21413     },
21414
21415     /**
21416      * Stops the repair animation if it's currently running
21417      */
21418     stop : function(){
21419         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21420             this.anim.stop();
21421         }
21422     },
21423
21424     /**
21425      * Displays this proxy
21426      */
21427     show : function(){
21428         this.el.show();
21429     },
21430
21431     /**
21432      * Force the Layer to sync its shadow and shim positions to the element
21433      */
21434     sync : function(){
21435         this.el.sync();
21436     },
21437
21438     /**
21439      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21440      * invalid drop operation by the item being dragged.
21441      * @param {Array} xy The XY position of the element ([x, y])
21442      * @param {Function} callback The function to call after the repair is complete
21443      * @param {Object} scope The scope in which to execute the callback
21444      */
21445     repair : function(xy, callback, scope){
21446         this.callback = callback;
21447         this.scope = scope;
21448         if(xy && this.animRepair !== false){
21449             this.el.addClass("x-dd-drag-repair");
21450             this.el.hideUnders(true);
21451             this.anim = this.el.shift({
21452                 duration: this.repairDuration || .5,
21453                 easing: 'easeOut',
21454                 xy: xy,
21455                 stopFx: true,
21456                 callback: this.afterRepair,
21457                 scope: this
21458             });
21459         }else{
21460             this.afterRepair();
21461         }
21462     },
21463
21464     // private
21465     afterRepair : function(){
21466         this.hide(true);
21467         if(typeof this.callback == "function"){
21468             this.callback.call(this.scope || this);
21469         }
21470         this.callback = null;
21471         this.scope = null;
21472     }
21473 };/*
21474  * Based on:
21475  * Ext JS Library 1.1.1
21476  * Copyright(c) 2006-2007, Ext JS, LLC.
21477  *
21478  * Originally Released Under LGPL - original licence link has changed is not relivant.
21479  *
21480  * Fork - LGPL
21481  * <script type="text/javascript">
21482  */
21483
21484 /**
21485  * @class Roo.dd.DragSource
21486  * @extends Roo.dd.DDProxy
21487  * A simple class that provides the basic implementation needed to make any element draggable.
21488  * @constructor
21489  * @param {String/HTMLElement/Element} el The container element
21490  * @param {Object} config
21491  */
21492 Roo.dd.DragSource = function(el, config){
21493     this.el = Roo.get(el);
21494     this.dragData = {};
21495     
21496     Roo.apply(this, config);
21497     
21498     if(!this.proxy){
21499         this.proxy = new Roo.dd.StatusProxy();
21500     }
21501
21502     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21503           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21504     
21505     this.dragging = false;
21506 };
21507
21508 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21509     /**
21510      * @cfg {String} dropAllowed
21511      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21512      */
21513     dropAllowed : "x-dd-drop-ok",
21514     /**
21515      * @cfg {String} dropNotAllowed
21516      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21517      */
21518     dropNotAllowed : "x-dd-drop-nodrop",
21519
21520     /**
21521      * Returns the data object associated with this drag source
21522      * @return {Object} data An object containing arbitrary data
21523      */
21524     getDragData : function(e){
21525         return this.dragData;
21526     },
21527
21528     // private
21529     onDragEnter : function(e, id){
21530         var target = Roo.dd.DragDropMgr.getDDById(id);
21531         this.cachedTarget = target;
21532         if(this.beforeDragEnter(target, e, id) !== false){
21533             if(target.isNotifyTarget){
21534                 var status = target.notifyEnter(this, e, this.dragData);
21535                 this.proxy.setStatus(status);
21536             }else{
21537                 this.proxy.setStatus(this.dropAllowed);
21538             }
21539             
21540             if(this.afterDragEnter){
21541                 /**
21542                  * An empty function by default, but provided so that you can perform a custom action
21543                  * when the dragged item enters the drop target by providing an implementation.
21544                  * @param {Roo.dd.DragDrop} target The drop target
21545                  * @param {Event} e The event object
21546                  * @param {String} id The id of the dragged element
21547                  * @method afterDragEnter
21548                  */
21549                 this.afterDragEnter(target, e, id);
21550             }
21551         }
21552     },
21553
21554     /**
21555      * An empty function by default, but provided so that you can perform a custom action
21556      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21557      * @param {Roo.dd.DragDrop} target The drop target
21558      * @param {Event} e The event object
21559      * @param {String} id The id of the dragged element
21560      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21561      */
21562     beforeDragEnter : function(target, e, id){
21563         return true;
21564     },
21565
21566     // private
21567     alignElWithMouse: function() {
21568         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21569         this.proxy.sync();
21570     },
21571
21572     // private
21573     onDragOver : function(e, id){
21574         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21575         if(this.beforeDragOver(target, e, id) !== false){
21576             if(target.isNotifyTarget){
21577                 var status = target.notifyOver(this, e, this.dragData);
21578                 this.proxy.setStatus(status);
21579             }
21580
21581             if(this.afterDragOver){
21582                 /**
21583                  * An empty function by default, but provided so that you can perform a custom action
21584                  * while the dragged item is over the drop target by providing an implementation.
21585                  * @param {Roo.dd.DragDrop} target The drop target
21586                  * @param {Event} e The event object
21587                  * @param {String} id The id of the dragged element
21588                  * @method afterDragOver
21589                  */
21590                 this.afterDragOver(target, e, id);
21591             }
21592         }
21593     },
21594
21595     /**
21596      * An empty function by default, but provided so that you can perform a custom action
21597      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21598      * @param {Roo.dd.DragDrop} target The drop target
21599      * @param {Event} e The event object
21600      * @param {String} id The id of the dragged element
21601      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21602      */
21603     beforeDragOver : function(target, e, id){
21604         return true;
21605     },
21606
21607     // private
21608     onDragOut : function(e, id){
21609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21610         if(this.beforeDragOut(target, e, id) !== false){
21611             if(target.isNotifyTarget){
21612                 target.notifyOut(this, e, this.dragData);
21613             }
21614             this.proxy.reset();
21615             if(this.afterDragOut){
21616                 /**
21617                  * An empty function by default, but provided so that you can perform a custom action
21618                  * after the dragged item is dragged out of the target without dropping.
21619                  * @param {Roo.dd.DragDrop} target The drop target
21620                  * @param {Event} e The event object
21621                  * @param {String} id The id of the dragged element
21622                  * @method afterDragOut
21623                  */
21624                 this.afterDragOut(target, e, id);
21625             }
21626         }
21627         this.cachedTarget = null;
21628     },
21629
21630     /**
21631      * An empty function by default, but provided so that you can perform a custom action before the dragged
21632      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21633      * @param {Roo.dd.DragDrop} target The drop target
21634      * @param {Event} e The event object
21635      * @param {String} id The id of the dragged element
21636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21637      */
21638     beforeDragOut : function(target, e, id){
21639         return true;
21640     },
21641     
21642     // private
21643     onDragDrop : function(e, id){
21644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21645         if(this.beforeDragDrop(target, e, id) !== false){
21646             if(target.isNotifyTarget){
21647                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21648                     this.onValidDrop(target, e, id);
21649                 }else{
21650                     this.onInvalidDrop(target, e, id);
21651                 }
21652             }else{
21653                 this.onValidDrop(target, e, id);
21654             }
21655             
21656             if(this.afterDragDrop){
21657                 /**
21658                  * An empty function by default, but provided so that you can perform a custom action
21659                  * after a valid drag drop has occurred by providing an implementation.
21660                  * @param {Roo.dd.DragDrop} target The drop target
21661                  * @param {Event} e The event object
21662                  * @param {String} id The id of the dropped element
21663                  * @method afterDragDrop
21664                  */
21665                 this.afterDragDrop(target, e, id);
21666             }
21667         }
21668         delete this.cachedTarget;
21669     },
21670
21671     /**
21672      * An empty function by default, but provided so that you can perform a custom action before the dragged
21673      * item is dropped onto the target and optionally cancel the onDragDrop.
21674      * @param {Roo.dd.DragDrop} target The drop target
21675      * @param {Event} e The event object
21676      * @param {String} id The id of the dragged element
21677      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21678      */
21679     beforeDragDrop : function(target, e, id){
21680         return true;
21681     },
21682
21683     // private
21684     onValidDrop : function(target, e, id){
21685         this.hideProxy();
21686         if(this.afterValidDrop){
21687             /**
21688              * An empty function by default, but provided so that you can perform a custom action
21689              * after a valid drop has occurred by providing an implementation.
21690              * @param {Object} target The target DD 
21691              * @param {Event} e The event object
21692              * @param {String} id The id of the dropped element
21693              * @method afterInvalidDrop
21694              */
21695             this.afterValidDrop(target, e, id);
21696         }
21697     },
21698
21699     // private
21700     getRepairXY : function(e, data){
21701         return this.el.getXY();  
21702     },
21703
21704     // private
21705     onInvalidDrop : function(target, e, id){
21706         this.beforeInvalidDrop(target, e, id);
21707         if(this.cachedTarget){
21708             if(this.cachedTarget.isNotifyTarget){
21709                 this.cachedTarget.notifyOut(this, e, this.dragData);
21710             }
21711             this.cacheTarget = null;
21712         }
21713         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21714
21715         if(this.afterInvalidDrop){
21716             /**
21717              * An empty function by default, but provided so that you can perform a custom action
21718              * after an invalid drop has occurred by providing an implementation.
21719              * @param {Event} e The event object
21720              * @param {String} id The id of the dropped element
21721              * @method afterInvalidDrop
21722              */
21723             this.afterInvalidDrop(e, id);
21724         }
21725     },
21726
21727     // private
21728     afterRepair : function(){
21729         if(Roo.enableFx){
21730             this.el.highlight(this.hlColor || "c3daf9");
21731         }
21732         this.dragging = false;
21733     },
21734
21735     /**
21736      * An empty function by default, but provided so that you can perform a custom action after an invalid
21737      * drop has occurred.
21738      * @param {Roo.dd.DragDrop} target The drop target
21739      * @param {Event} e The event object
21740      * @param {String} id The id of the dragged element
21741      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21742      */
21743     beforeInvalidDrop : function(target, e, id){
21744         return true;
21745     },
21746
21747     // private
21748     handleMouseDown : function(e){
21749         if(this.dragging) {
21750             return;
21751         }
21752         var data = this.getDragData(e);
21753         if(data && this.onBeforeDrag(data, e) !== false){
21754             this.dragData = data;
21755             this.proxy.stop();
21756             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21757         } 
21758     },
21759
21760     /**
21761      * An empty function by default, but provided so that you can perform a custom action before the initial
21762      * drag event begins and optionally cancel it.
21763      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21764      * @param {Event} e The event object
21765      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21766      */
21767     onBeforeDrag : function(data, e){
21768         return true;
21769     },
21770
21771     /**
21772      * An empty function by default, but provided so that you can perform a custom action once the initial
21773      * drag event has begun.  The drag cannot be canceled from this function.
21774      * @param {Number} x The x position of the click on the dragged object
21775      * @param {Number} y The y position of the click on the dragged object
21776      */
21777     onStartDrag : Roo.emptyFn,
21778
21779     // private - YUI override
21780     startDrag : function(x, y){
21781         this.proxy.reset();
21782         this.dragging = true;
21783         this.proxy.update("");
21784         this.onInitDrag(x, y);
21785         this.proxy.show();
21786     },
21787
21788     // private
21789     onInitDrag : function(x, y){
21790         var clone = this.el.dom.cloneNode(true);
21791         clone.id = Roo.id(); // prevent duplicate ids
21792         this.proxy.update(clone);
21793         this.onStartDrag(x, y);
21794         return true;
21795     },
21796
21797     /**
21798      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21799      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21800      */
21801     getProxy : function(){
21802         return this.proxy;  
21803     },
21804
21805     /**
21806      * Hides the drag source's {@link Roo.dd.StatusProxy}
21807      */
21808     hideProxy : function(){
21809         this.proxy.hide();  
21810         this.proxy.reset(true);
21811         this.dragging = false;
21812     },
21813
21814     // private
21815     triggerCacheRefresh : function(){
21816         Roo.dd.DDM.refreshCache(this.groups);
21817     },
21818
21819     // private - override to prevent hiding
21820     b4EndDrag: function(e) {
21821     },
21822
21823     // private - override to prevent moving
21824     endDrag : function(e){
21825         this.onEndDrag(this.dragData, e);
21826     },
21827
21828     // private
21829     onEndDrag : function(data, e){
21830     },
21831     
21832     // private - pin to cursor
21833     autoOffset : function(x, y) {
21834         this.setDelta(-12, -20);
21835     }    
21836 });/*
21837  * Based on:
21838  * Ext JS Library 1.1.1
21839  * Copyright(c) 2006-2007, Ext JS, LLC.
21840  *
21841  * Originally Released Under LGPL - original licence link has changed is not relivant.
21842  *
21843  * Fork - LGPL
21844  * <script type="text/javascript">
21845  */
21846
21847
21848 /**
21849  * @class Roo.dd.DropTarget
21850  * @extends Roo.dd.DDTarget
21851  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21852  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21853  * @constructor
21854  * @param {String/HTMLElement/Element} el The container element
21855  * @param {Object} config
21856  */
21857 Roo.dd.DropTarget = function(el, config){
21858     this.el = Roo.get(el);
21859     
21860     var listeners = false; ;
21861     if (config && config.listeners) {
21862         listeners= config.listeners;
21863         delete config.listeners;
21864     }
21865     Roo.apply(this, config);
21866     
21867     if(this.containerScroll){
21868         Roo.dd.ScrollManager.register(this.el);
21869     }
21870     this.addEvents( {
21871          /**
21872          * @scope Roo.dd.DropTarget
21873          */
21874          
21875          /**
21876          * @event enter
21877          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21878          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21879          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21880          * 
21881          * IMPORTANT : it should set this.overClass and this.dropAllowed
21882          * 
21883          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21884          * @param {Event} e The event
21885          * @param {Object} data An object containing arbitrary data supplied by the drag source
21886          */
21887         "enter" : true,
21888         
21889          /**
21890          * @event over
21891          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21892          * This method will be called on every mouse movement while the drag source is over the drop target.
21893          * This default implementation simply returns the dropAllowed config value.
21894          * 
21895          * IMPORTANT : it should set this.dropAllowed
21896          * 
21897          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21898          * @param {Event} e The event
21899          * @param {Object} data An object containing arbitrary data supplied by the drag source
21900          
21901          */
21902         "over" : true,
21903         /**
21904          * @event out
21905          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21906          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21907          * overClass (if any) from the drop element.
21908          * 
21909          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21910          * @param {Event} e The event
21911          * @param {Object} data An object containing arbitrary data supplied by the drag source
21912          */
21913          "out" : true,
21914          
21915         /**
21916          * @event drop
21917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21918          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21919          * implementation that does something to process the drop event and returns true so that the drag source's
21920          * repair action does not run.
21921          * 
21922          * IMPORTANT : it should set this.success
21923          * 
21924          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21925          * @param {Event} e The event
21926          * @param {Object} data An object containing arbitrary data supplied by the drag source
21927         */
21928          "drop" : true
21929     });
21930             
21931      
21932     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21933         this.el.dom, 
21934         this.ddGroup || this.group,
21935         {
21936             isTarget: true,
21937             listeners : listeners || {} 
21938            
21939         
21940         }
21941     );
21942
21943 };
21944
21945 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21946     /**
21947      * @cfg {String} overClass
21948      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21949      */
21950      /**
21951      * @cfg {String} ddGroup
21952      * The drag drop group to handle drop events for
21953      */
21954      
21955     /**
21956      * @cfg {String} dropAllowed
21957      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21958      */
21959     dropAllowed : "x-dd-drop-ok",
21960     /**
21961      * @cfg {String} dropNotAllowed
21962      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21963      */
21964     dropNotAllowed : "x-dd-drop-nodrop",
21965     /**
21966      * @cfg {boolean} success
21967      * set this after drop listener.. 
21968      */
21969     success : false,
21970     /**
21971      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21972      * if the drop point is valid for over/enter..
21973      */
21974     valid : false,
21975     // private
21976     isTarget : true,
21977
21978     // private
21979     isNotifyTarget : true,
21980     
21981     /**
21982      * @hide
21983      */
21984     notifyEnter : function(dd, e, data)
21985     {
21986         this.valid = true;
21987         this.fireEvent('enter', dd, e, data);
21988         if(this.overClass){
21989             this.el.addClass(this.overClass);
21990         }
21991         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21992             this.valid ? this.dropAllowed : this.dropNotAllowed
21993         );
21994     },
21995
21996     /**
21997      * @hide
21998      */
21999     notifyOver : function(dd, e, data)
22000     {
22001         this.valid = true;
22002         this.fireEvent('over', dd, e, data);
22003         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22004             this.valid ? this.dropAllowed : this.dropNotAllowed
22005         );
22006     },
22007
22008     /**
22009      * @hide
22010      */
22011     notifyOut : function(dd, e, data)
22012     {
22013         this.fireEvent('out', dd, e, data);
22014         if(this.overClass){
22015             this.el.removeClass(this.overClass);
22016         }
22017     },
22018
22019     /**
22020      * @hide
22021      */
22022     notifyDrop : function(dd, e, data)
22023     {
22024         this.success = false;
22025         this.fireEvent('drop', dd, e, data);
22026         return this.success;
22027     }
22028 });/*
22029  * Based on:
22030  * Ext JS Library 1.1.1
22031  * Copyright(c) 2006-2007, Ext JS, LLC.
22032  *
22033  * Originally Released Under LGPL - original licence link has changed is not relivant.
22034  *
22035  * Fork - LGPL
22036  * <script type="text/javascript">
22037  */
22038
22039
22040 /**
22041  * @class Roo.dd.DragZone
22042  * @extends Roo.dd.DragSource
22043  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22044  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22045  * @constructor
22046  * @param {String/HTMLElement/Element} el The container element
22047  * @param {Object} config
22048  */
22049 Roo.dd.DragZone = function(el, config){
22050     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22051     if(this.containerScroll){
22052         Roo.dd.ScrollManager.register(this.el);
22053     }
22054 };
22055
22056 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22057     /**
22058      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22059      * for auto scrolling during drag operations.
22060      */
22061     /**
22062      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22063      * method after a failed drop (defaults to "c3daf9" - light blue)
22064      */
22065
22066     /**
22067      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22068      * for a valid target to drag based on the mouse down. Override this method
22069      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22070      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22071      * @param {EventObject} e The mouse down event
22072      * @return {Object} The dragData
22073      */
22074     getDragData : function(e){
22075         return Roo.dd.Registry.getHandleFromEvent(e);
22076     },
22077     
22078     /**
22079      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22080      * this.dragData.ddel
22081      * @param {Number} x The x position of the click on the dragged object
22082      * @param {Number} y The y position of the click on the dragged object
22083      * @return {Boolean} true to continue the drag, false to cancel
22084      */
22085     onInitDrag : function(x, y){
22086         this.proxy.update(this.dragData.ddel.cloneNode(true));
22087         this.onStartDrag(x, y);
22088         return true;
22089     },
22090     
22091     /**
22092      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22093      */
22094     afterRepair : function(){
22095         if(Roo.enableFx){
22096             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22097         }
22098         this.dragging = false;
22099     },
22100
22101     /**
22102      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22103      * the XY of this.dragData.ddel
22104      * @param {EventObject} e The mouse up event
22105      * @return {Array} The xy location (e.g. [100, 200])
22106      */
22107     getRepairXY : function(e){
22108         return Roo.Element.fly(this.dragData.ddel).getXY();  
22109     }
22110 });/*
22111  * Based on:
22112  * Ext JS Library 1.1.1
22113  * Copyright(c) 2006-2007, Ext JS, LLC.
22114  *
22115  * Originally Released Under LGPL - original licence link has changed is not relivant.
22116  *
22117  * Fork - LGPL
22118  * <script type="text/javascript">
22119  */
22120 /**
22121  * @class Roo.dd.DropZone
22122  * @extends Roo.dd.DropTarget
22123  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22124  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22125  * @constructor
22126  * @param {String/HTMLElement/Element} el The container element
22127  * @param {Object} config
22128  */
22129 Roo.dd.DropZone = function(el, config){
22130     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22131 };
22132
22133 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22134     /**
22135      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22136      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22137      * provide your own custom lookup.
22138      * @param {Event} e The event
22139      * @return {Object} data The custom data
22140      */
22141     getTargetFromEvent : function(e){
22142         return Roo.dd.Registry.getTargetFromEvent(e);
22143     },
22144
22145     /**
22146      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22147      * that it has registered.  This method has no default implementation and should be overridden to provide
22148      * node-specific processing if necessary.
22149      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22150      * {@link #getTargetFromEvent} for this node)
22151      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22152      * @param {Event} e The event
22153      * @param {Object} data An object containing arbitrary data supplied by the drag source
22154      */
22155     onNodeEnter : function(n, dd, e, data){
22156         
22157     },
22158
22159     /**
22160      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22161      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22162      * overridden to provide the proper feedback.
22163      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22164      * {@link #getTargetFromEvent} for this node)
22165      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22166      * @param {Event} e The event
22167      * @param {Object} data An object containing arbitrary data supplied by the drag source
22168      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22169      * underlying {@link Roo.dd.StatusProxy} can be updated
22170      */
22171     onNodeOver : function(n, dd, e, data){
22172         return this.dropAllowed;
22173     },
22174
22175     /**
22176      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22177      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22178      * node-specific processing if necessary.
22179      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22180      * {@link #getTargetFromEvent} for this node)
22181      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22182      * @param {Event} e The event
22183      * @param {Object} data An object containing arbitrary data supplied by the drag source
22184      */
22185     onNodeOut : function(n, dd, e, data){
22186         
22187     },
22188
22189     /**
22190      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22191      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22192      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22193      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22194      * {@link #getTargetFromEvent} for this node)
22195      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22196      * @param {Event} e The event
22197      * @param {Object} data An object containing arbitrary data supplied by the drag source
22198      * @return {Boolean} True if the drop was valid, else false
22199      */
22200     onNodeDrop : function(n, dd, e, data){
22201         return false;
22202     },
22203
22204     /**
22205      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22206      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22207      * it should be overridden to provide the proper feedback if necessary.
22208      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22209      * @param {Event} e The event
22210      * @param {Object} data An object containing arbitrary data supplied by the drag source
22211      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22212      * underlying {@link Roo.dd.StatusProxy} can be updated
22213      */
22214     onContainerOver : function(dd, e, data){
22215         return this.dropNotAllowed;
22216     },
22217
22218     /**
22219      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22220      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22221      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22222      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22223      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22224      * @param {Event} e The event
22225      * @param {Object} data An object containing arbitrary data supplied by the drag source
22226      * @return {Boolean} True if the drop was valid, else false
22227      */
22228     onContainerDrop : function(dd, e, data){
22229         return false;
22230     },
22231
22232     /**
22233      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22234      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22235      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22236      * you should override this method and provide a custom implementation.
22237      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22238      * @param {Event} e The event
22239      * @param {Object} data An object containing arbitrary data supplied by the drag source
22240      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22241      * underlying {@link Roo.dd.StatusProxy} can be updated
22242      */
22243     notifyEnter : function(dd, e, data){
22244         return this.dropNotAllowed;
22245     },
22246
22247     /**
22248      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22249      * This method will be called on every mouse movement while the drag source is over the drop zone.
22250      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22251      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22252      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22253      * registered node, it will call {@link #onContainerOver}.
22254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22255      * @param {Event} e The event
22256      * @param {Object} data An object containing arbitrary data supplied by the drag source
22257      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22258      * underlying {@link Roo.dd.StatusProxy} can be updated
22259      */
22260     notifyOver : function(dd, e, data){
22261         var n = this.getTargetFromEvent(e);
22262         if(!n){ // not over valid drop target
22263             if(this.lastOverNode){
22264                 this.onNodeOut(this.lastOverNode, dd, e, data);
22265                 this.lastOverNode = null;
22266             }
22267             return this.onContainerOver(dd, e, data);
22268         }
22269         if(this.lastOverNode != n){
22270             if(this.lastOverNode){
22271                 this.onNodeOut(this.lastOverNode, dd, e, data);
22272             }
22273             this.onNodeEnter(n, dd, e, data);
22274             this.lastOverNode = n;
22275         }
22276         return this.onNodeOver(n, dd, e, data);
22277     },
22278
22279     /**
22280      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22281      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22282      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22283      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22284      * @param {Event} e The event
22285      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22286      */
22287     notifyOut : function(dd, e, data){
22288         if(this.lastOverNode){
22289             this.onNodeOut(this.lastOverNode, dd, e, data);
22290             this.lastOverNode = null;
22291         }
22292     },
22293
22294     /**
22295      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22296      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22297      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22298      * otherwise it will call {@link #onContainerDrop}.
22299      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22300      * @param {Event} e The event
22301      * @param {Object} data An object containing arbitrary data supplied by the drag source
22302      * @return {Boolean} True if the drop was valid, else false
22303      */
22304     notifyDrop : function(dd, e, data){
22305         if(this.lastOverNode){
22306             this.onNodeOut(this.lastOverNode, dd, e, data);
22307             this.lastOverNode = null;
22308         }
22309         var n = this.getTargetFromEvent(e);
22310         return n ?
22311             this.onNodeDrop(n, dd, e, data) :
22312             this.onContainerDrop(dd, e, data);
22313     },
22314
22315     // private
22316     triggerCacheRefresh : function(){
22317         Roo.dd.DDM.refreshCache(this.groups);
22318     }  
22319 });/*
22320  * Based on:
22321  * Ext JS Library 1.1.1
22322  * Copyright(c) 2006-2007, Ext JS, LLC.
22323  *
22324  * Originally Released Under LGPL - original licence link has changed is not relivant.
22325  *
22326  * Fork - LGPL
22327  * <script type="text/javascript">
22328  */
22329
22330
22331 /**
22332  * @class Roo.data.SortTypes
22333  * @singleton
22334  * Defines the default sorting (casting?) comparison functions used when sorting data.
22335  */
22336 Roo.data.SortTypes = {
22337     /**
22338      * Default sort that does nothing
22339      * @param {Mixed} s The value being converted
22340      * @return {Mixed} The comparison value
22341      */
22342     none : function(s){
22343         return s;
22344     },
22345     
22346     /**
22347      * The regular expression used to strip tags
22348      * @type {RegExp}
22349      * @property
22350      */
22351     stripTagsRE : /<\/?[^>]+>/gi,
22352     
22353     /**
22354      * Strips all HTML tags to sort on text only
22355      * @param {Mixed} s The value being converted
22356      * @return {String} The comparison value
22357      */
22358     asText : function(s){
22359         return String(s).replace(this.stripTagsRE, "");
22360     },
22361     
22362     /**
22363      * Strips all HTML tags to sort on text only - Case insensitive
22364      * @param {Mixed} s The value being converted
22365      * @return {String} The comparison value
22366      */
22367     asUCText : function(s){
22368         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22369     },
22370     
22371     /**
22372      * Case insensitive string
22373      * @param {Mixed} s The value being converted
22374      * @return {String} The comparison value
22375      */
22376     asUCString : function(s) {
22377         return String(s).toUpperCase();
22378     },
22379     
22380     /**
22381      * Date sorting
22382      * @param {Mixed} s The value being converted
22383      * @return {Number} The comparison value
22384      */
22385     asDate : function(s) {
22386         if(!s){
22387             return 0;
22388         }
22389         if(s instanceof Date){
22390             return s.getTime();
22391         }
22392         return Date.parse(String(s));
22393     },
22394     
22395     /**
22396      * Float sorting
22397      * @param {Mixed} s The value being converted
22398      * @return {Float} The comparison value
22399      */
22400     asFloat : function(s) {
22401         var val = parseFloat(String(s).replace(/,/g, ""));
22402         if(isNaN(val)) {
22403             val = 0;
22404         }
22405         return val;
22406     },
22407     
22408     /**
22409      * Integer sorting
22410      * @param {Mixed} s The value being converted
22411      * @return {Number} The comparison value
22412      */
22413     asInt : function(s) {
22414         var val = parseInt(String(s).replace(/,/g, ""));
22415         if(isNaN(val)) {
22416             val = 0;
22417         }
22418         return val;
22419     }
22420 };/*
22421  * Based on:
22422  * Ext JS Library 1.1.1
22423  * Copyright(c) 2006-2007, Ext JS, LLC.
22424  *
22425  * Originally Released Under LGPL - original licence link has changed is not relivant.
22426  *
22427  * Fork - LGPL
22428  * <script type="text/javascript">
22429  */
22430
22431 /**
22432 * @class Roo.data.Record
22433  * Instances of this class encapsulate both record <em>definition</em> information, and record
22434  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22435  * to access Records cached in an {@link Roo.data.Store} object.<br>
22436  * <p>
22437  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22438  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22439  * objects.<br>
22440  * <p>
22441  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22442  * @constructor
22443  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22444  * {@link #create}. The parameters are the same.
22445  * @param {Array} data An associative Array of data values keyed by the field name.
22446  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22447  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22448  * not specified an integer id is generated.
22449  */
22450 Roo.data.Record = function(data, id){
22451     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22452     this.data = data;
22453 };
22454
22455 /**
22456  * Generate a constructor for a specific record layout.
22457  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22458  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22459  * Each field definition object may contain the following properties: <ul>
22460  * <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,
22461  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22462  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22463  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22464  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22465  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22466  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22467  * this may be omitted.</p></li>
22468  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22469  * <ul><li>auto (Default, implies no conversion)</li>
22470  * <li>string</li>
22471  * <li>int</li>
22472  * <li>float</li>
22473  * <li>boolean</li>
22474  * <li>date</li></ul></p></li>
22475  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22476  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22477  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22478  * by the Reader into an object that will be stored in the Record. It is passed the
22479  * following parameters:<ul>
22480  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22481  * </ul></p></li>
22482  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22483  * </ul>
22484  * <br>usage:<br><pre><code>
22485 var TopicRecord = Roo.data.Record.create(
22486     {name: 'title', mapping: 'topic_title'},
22487     {name: 'author', mapping: 'username'},
22488     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22489     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22490     {name: 'lastPoster', mapping: 'user2'},
22491     {name: 'excerpt', mapping: 'post_text'}
22492 );
22493
22494 var myNewRecord = new TopicRecord({
22495     title: 'Do my job please',
22496     author: 'noobie',
22497     totalPosts: 1,
22498     lastPost: new Date(),
22499     lastPoster: 'Animal',
22500     excerpt: 'No way dude!'
22501 });
22502 myStore.add(myNewRecord);
22503 </code></pre>
22504  * @method create
22505  * @static
22506  */
22507 Roo.data.Record.create = function(o){
22508     var f = function(){
22509         f.superclass.constructor.apply(this, arguments);
22510     };
22511     Roo.extend(f, Roo.data.Record);
22512     var p = f.prototype;
22513     p.fields = new Roo.util.MixedCollection(false, function(field){
22514         return field.name;
22515     });
22516     for(var i = 0, len = o.length; i < len; i++){
22517         p.fields.add(new Roo.data.Field(o[i]));
22518     }
22519     f.getField = function(name){
22520         return p.fields.get(name);  
22521     };
22522     return f;
22523 };
22524
22525 Roo.data.Record.AUTO_ID = 1000;
22526 Roo.data.Record.EDIT = 'edit';
22527 Roo.data.Record.REJECT = 'reject';
22528 Roo.data.Record.COMMIT = 'commit';
22529
22530 Roo.data.Record.prototype = {
22531     /**
22532      * Readonly flag - true if this record has been modified.
22533      * @type Boolean
22534      */
22535     dirty : false,
22536     editing : false,
22537     error: null,
22538     modified: null,
22539
22540     // private
22541     join : function(store){
22542         this.store = store;
22543     },
22544
22545     /**
22546      * Set the named field to the specified value.
22547      * @param {String} name The name of the field to set.
22548      * @param {Object} value The value to set the field to.
22549      */
22550     set : function(name, value){
22551         if(this.data[name] == value){
22552             return;
22553         }
22554         this.dirty = true;
22555         if(!this.modified){
22556             this.modified = {};
22557         }
22558         if(typeof this.modified[name] == 'undefined'){
22559             this.modified[name] = this.data[name];
22560         }
22561         this.data[name] = value;
22562         if(!this.editing && this.store){
22563             this.store.afterEdit(this);
22564         }       
22565     },
22566
22567     /**
22568      * Get the value of the named field.
22569      * @param {String} name The name of the field to get the value of.
22570      * @return {Object} The value of the field.
22571      */
22572     get : function(name){
22573         return this.data[name]; 
22574     },
22575
22576     // private
22577     beginEdit : function(){
22578         this.editing = true;
22579         this.modified = {}; 
22580     },
22581
22582     // private
22583     cancelEdit : function(){
22584         this.editing = false;
22585         delete this.modified;
22586     },
22587
22588     // private
22589     endEdit : function(){
22590         this.editing = false;
22591         if(this.dirty && this.store){
22592             this.store.afterEdit(this);
22593         }
22594     },
22595
22596     /**
22597      * Usually called by the {@link Roo.data.Store} which owns the Record.
22598      * Rejects all changes made to the Record since either creation, or the last commit operation.
22599      * Modified fields are reverted to their original values.
22600      * <p>
22601      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22602      * of reject operations.
22603      */
22604     reject : function(){
22605         var m = this.modified;
22606         for(var n in m){
22607             if(typeof m[n] != "function"){
22608                 this.data[n] = m[n];
22609             }
22610         }
22611         this.dirty = false;
22612         delete this.modified;
22613         this.editing = false;
22614         if(this.store){
22615             this.store.afterReject(this);
22616         }
22617     },
22618
22619     /**
22620      * Usually called by the {@link Roo.data.Store} which owns the Record.
22621      * Commits all changes made to the Record since either creation, or the last commit operation.
22622      * <p>
22623      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22624      * of commit operations.
22625      */
22626     commit : function(){
22627         this.dirty = false;
22628         delete this.modified;
22629         this.editing = false;
22630         if(this.store){
22631             this.store.afterCommit(this);
22632         }
22633     },
22634
22635     // private
22636     hasError : function(){
22637         return this.error != null;
22638     },
22639
22640     // private
22641     clearError : function(){
22642         this.error = null;
22643     },
22644
22645     /**
22646      * Creates a copy of this record.
22647      * @param {String} id (optional) A new record id if you don't want to use this record's id
22648      * @return {Record}
22649      */
22650     copy : function(newId) {
22651         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22652     }
22653 };/*
22654  * Based on:
22655  * Ext JS Library 1.1.1
22656  * Copyright(c) 2006-2007, Ext JS, LLC.
22657  *
22658  * Originally Released Under LGPL - original licence link has changed is not relivant.
22659  *
22660  * Fork - LGPL
22661  * <script type="text/javascript">
22662  */
22663
22664
22665
22666 /**
22667  * @class Roo.data.Store
22668  * @extends Roo.util.Observable
22669  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22670  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22671  * <p>
22672  * 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
22673  * has no knowledge of the format of the data returned by the Proxy.<br>
22674  * <p>
22675  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22676  * instances from the data object. These records are cached and made available through accessor functions.
22677  * @constructor
22678  * Creates a new Store.
22679  * @param {Object} config A config object containing the objects needed for the Store to access data,
22680  * and read the data into Records.
22681  */
22682 Roo.data.Store = function(config){
22683     this.data = new Roo.util.MixedCollection(false);
22684     this.data.getKey = function(o){
22685         return o.id;
22686     };
22687     this.baseParams = {};
22688     // private
22689     this.paramNames = {
22690         "start" : "start",
22691         "limit" : "limit",
22692         "sort" : "sort",
22693         "dir" : "dir",
22694         "multisort" : "_multisort"
22695     };
22696
22697     if(config && config.data){
22698         this.inlineData = config.data;
22699         delete config.data;
22700     }
22701
22702     Roo.apply(this, config);
22703     
22704     if(this.reader){ // reader passed
22705         this.reader = Roo.factory(this.reader, Roo.data);
22706         this.reader.xmodule = this.xmodule || false;
22707         if(!this.recordType){
22708             this.recordType = this.reader.recordType;
22709         }
22710         if(this.reader.onMetaChange){
22711             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22712         }
22713     }
22714
22715     if(this.recordType){
22716         this.fields = this.recordType.prototype.fields;
22717     }
22718     this.modified = [];
22719
22720     this.addEvents({
22721         /**
22722          * @event datachanged
22723          * Fires when the data cache has changed, and a widget which is using this Store
22724          * as a Record cache should refresh its view.
22725          * @param {Store} this
22726          */
22727         datachanged : true,
22728         /**
22729          * @event metachange
22730          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22731          * @param {Store} this
22732          * @param {Object} meta The JSON metadata
22733          */
22734         metachange : true,
22735         /**
22736          * @event add
22737          * Fires when Records have been added to the Store
22738          * @param {Store} this
22739          * @param {Roo.data.Record[]} records The array of Records added
22740          * @param {Number} index The index at which the record(s) were added
22741          */
22742         add : true,
22743         /**
22744          * @event remove
22745          * Fires when a Record has been removed from the Store
22746          * @param {Store} this
22747          * @param {Roo.data.Record} record The Record that was removed
22748          * @param {Number} index The index at which the record was removed
22749          */
22750         remove : true,
22751         /**
22752          * @event update
22753          * Fires when a Record has been updated
22754          * @param {Store} this
22755          * @param {Roo.data.Record} record The Record that was updated
22756          * @param {String} operation The update operation being performed.  Value may be one of:
22757          * <pre><code>
22758  Roo.data.Record.EDIT
22759  Roo.data.Record.REJECT
22760  Roo.data.Record.COMMIT
22761          * </code></pre>
22762          */
22763         update : true,
22764         /**
22765          * @event clear
22766          * Fires when the data cache has been cleared.
22767          * @param {Store} this
22768          */
22769         clear : true,
22770         /**
22771          * @event beforeload
22772          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22773          * the load action will be canceled.
22774          * @param {Store} this
22775          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22776          */
22777         beforeload : true,
22778         /**
22779          * @event beforeloadadd
22780          * Fires after a new set of Records has been loaded.
22781          * @param {Store} this
22782          * @param {Roo.data.Record[]} records The Records that were loaded
22783          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22784          */
22785         beforeloadadd : true,
22786         /**
22787          * @event load
22788          * Fires after a new set of Records has been loaded, before they are added to the store.
22789          * @param {Store} this
22790          * @param {Roo.data.Record[]} records The Records that were loaded
22791          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22792          * @params {Object} return from reader
22793          */
22794         load : true,
22795         /**
22796          * @event loadexception
22797          * Fires if an exception occurs in the Proxy during loading.
22798          * Called with the signature of the Proxy's "loadexception" event.
22799          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22800          * 
22801          * @param {Proxy} 
22802          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22803          * @param {Object} load options 
22804          * @param {Object} jsonData from your request (normally this contains the Exception)
22805          */
22806         loadexception : true
22807     });
22808     
22809     if(this.proxy){
22810         this.proxy = Roo.factory(this.proxy, Roo.data);
22811         this.proxy.xmodule = this.xmodule || false;
22812         this.relayEvents(this.proxy,  ["loadexception"]);
22813     }
22814     this.sortToggle = {};
22815     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22816
22817     Roo.data.Store.superclass.constructor.call(this);
22818
22819     if(this.inlineData){
22820         this.loadData(this.inlineData);
22821         delete this.inlineData;
22822     }
22823 };
22824
22825 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22826      /**
22827     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22828     * without a remote query - used by combo/forms at present.
22829     */
22830     
22831     /**
22832     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22833     */
22834     /**
22835     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22836     */
22837     /**
22838     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22839     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22840     */
22841     /**
22842     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22843     * on any HTTP request
22844     */
22845     /**
22846     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22847     */
22848     /**
22849     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22850     */
22851     multiSort: false,
22852     /**
22853     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22854     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22855     */
22856     remoteSort : false,
22857
22858     /**
22859     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22860      * loaded or when a record is removed. (defaults to false).
22861     */
22862     pruneModifiedRecords : false,
22863
22864     // private
22865     lastOptions : null,
22866
22867     /**
22868      * Add Records to the Store and fires the add event.
22869      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22870      */
22871     add : function(records){
22872         records = [].concat(records);
22873         for(var i = 0, len = records.length; i < len; i++){
22874             records[i].join(this);
22875         }
22876         var index = this.data.length;
22877         this.data.addAll(records);
22878         this.fireEvent("add", this, records, index);
22879     },
22880
22881     /**
22882      * Remove a Record from the Store and fires the remove event.
22883      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22884      */
22885     remove : function(record){
22886         var index = this.data.indexOf(record);
22887         this.data.removeAt(index);
22888         if(this.pruneModifiedRecords){
22889             this.modified.remove(record);
22890         }
22891         this.fireEvent("remove", this, record, index);
22892     },
22893
22894     /**
22895      * Remove all Records from the Store and fires the clear event.
22896      */
22897     removeAll : function(){
22898         this.data.clear();
22899         if(this.pruneModifiedRecords){
22900             this.modified = [];
22901         }
22902         this.fireEvent("clear", this);
22903     },
22904
22905     /**
22906      * Inserts Records to the Store at the given index and fires the add event.
22907      * @param {Number} index The start index at which to insert the passed Records.
22908      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22909      */
22910     insert : function(index, records){
22911         records = [].concat(records);
22912         for(var i = 0, len = records.length; i < len; i++){
22913             this.data.insert(index, records[i]);
22914             records[i].join(this);
22915         }
22916         this.fireEvent("add", this, records, index);
22917     },
22918
22919     /**
22920      * Get the index within the cache of the passed Record.
22921      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22922      * @return {Number} The index of the passed Record. Returns -1 if not found.
22923      */
22924     indexOf : function(record){
22925         return this.data.indexOf(record);
22926     },
22927
22928     /**
22929      * Get the index within the cache of the Record with the passed id.
22930      * @param {String} id The id of the Record to find.
22931      * @return {Number} The index of the Record. Returns -1 if not found.
22932      */
22933     indexOfId : function(id){
22934         return this.data.indexOfKey(id);
22935     },
22936
22937     /**
22938      * Get the Record with the specified id.
22939      * @param {String} id The id of the Record to find.
22940      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22941      */
22942     getById : function(id){
22943         return this.data.key(id);
22944     },
22945
22946     /**
22947      * Get the Record at the specified index.
22948      * @param {Number} index The index of the Record to find.
22949      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22950      */
22951     getAt : function(index){
22952         return this.data.itemAt(index);
22953     },
22954
22955     /**
22956      * Returns a range of Records between specified indices.
22957      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22958      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22959      * @return {Roo.data.Record[]} An array of Records
22960      */
22961     getRange : function(start, end){
22962         return this.data.getRange(start, end);
22963     },
22964
22965     // private
22966     storeOptions : function(o){
22967         o = Roo.apply({}, o);
22968         delete o.callback;
22969         delete o.scope;
22970         this.lastOptions = o;
22971     },
22972
22973     /**
22974      * Loads the Record cache from the configured Proxy using the configured Reader.
22975      * <p>
22976      * If using remote paging, then the first load call must specify the <em>start</em>
22977      * and <em>limit</em> properties in the options.params property to establish the initial
22978      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22979      * <p>
22980      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22981      * and this call will return before the new data has been loaded. Perform any post-processing
22982      * in a callback function, or in a "load" event handler.</strong>
22983      * <p>
22984      * @param {Object} options An object containing properties which control loading options:<ul>
22985      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22986      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22987      * passed the following arguments:<ul>
22988      * <li>r : Roo.data.Record[]</li>
22989      * <li>options: Options object from the load call</li>
22990      * <li>success: Boolean success indicator</li></ul></li>
22991      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22992      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22993      * </ul>
22994      */
22995     load : function(options){
22996         options = options || {};
22997         if(this.fireEvent("beforeload", this, options) !== false){
22998             this.storeOptions(options);
22999             var p = Roo.apply(options.params || {}, this.baseParams);
23000             // if meta was not loaded from remote source.. try requesting it.
23001             if (!this.reader.metaFromRemote) {
23002                 p._requestMeta = 1;
23003             }
23004             if(this.sortInfo && this.remoteSort){
23005                 var pn = this.paramNames;
23006                 p[pn["sort"]] = this.sortInfo.field;
23007                 p[pn["dir"]] = this.sortInfo.direction;
23008             }
23009             if (this.multiSort) {
23010                 var pn = this.paramNames;
23011                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23012             }
23013             
23014             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23015         }
23016     },
23017
23018     /**
23019      * Reloads the Record cache from the configured Proxy using the configured Reader and
23020      * the options from the last load operation performed.
23021      * @param {Object} options (optional) An object containing properties which may override the options
23022      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23023      * the most recently used options are reused).
23024      */
23025     reload : function(options){
23026         this.load(Roo.applyIf(options||{}, this.lastOptions));
23027     },
23028
23029     // private
23030     // Called as a callback by the Reader during a load operation.
23031     loadRecords : function(o, options, success){
23032         if(!o || success === false){
23033             if(success !== false){
23034                 this.fireEvent("load", this, [], options, o);
23035             }
23036             if(options.callback){
23037                 options.callback.call(options.scope || this, [], options, false);
23038             }
23039             return;
23040         }
23041         // if data returned failure - throw an exception.
23042         if (o.success === false) {
23043             // show a message if no listener is registered.
23044             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23045                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23046             }
23047             // loadmask wil be hooked into this..
23048             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23049             return;
23050         }
23051         var r = o.records, t = o.totalRecords || r.length;
23052         
23053         this.fireEvent("beforeloadadd", this, r, options, o);
23054         
23055         if(!options || options.add !== true){
23056             if(this.pruneModifiedRecords){
23057                 this.modified = [];
23058             }
23059             for(var i = 0, len = r.length; i < len; i++){
23060                 r[i].join(this);
23061             }
23062             if(this.snapshot){
23063                 this.data = this.snapshot;
23064                 delete this.snapshot;
23065             }
23066             this.data.clear();
23067             this.data.addAll(r);
23068             this.totalLength = t;
23069             this.applySort();
23070             this.fireEvent("datachanged", this);
23071         }else{
23072             this.totalLength = Math.max(t, this.data.length+r.length);
23073             this.add(r);
23074         }
23075         this.fireEvent("load", this, r, options, o);
23076         if(options.callback){
23077             options.callback.call(options.scope || this, r, options, true);
23078         }
23079     },
23080
23081
23082     /**
23083      * Loads data from a passed data block. A Reader which understands the format of the data
23084      * must have been configured in the constructor.
23085      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23086      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23087      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23088      */
23089     loadData : function(o, append){
23090         var r = this.reader.readRecords(o);
23091         this.loadRecords(r, {add: append}, true);
23092     },
23093
23094     /**
23095      * Gets the number of cached records.
23096      * <p>
23097      * <em>If using paging, this may not be the total size of the dataset. If the data object
23098      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23099      * the data set size</em>
23100      */
23101     getCount : function(){
23102         return this.data.length || 0;
23103     },
23104
23105     /**
23106      * Gets the total number of records in the dataset as returned by the server.
23107      * <p>
23108      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23109      * the dataset size</em>
23110      */
23111     getTotalCount : function(){
23112         return this.totalLength || 0;
23113     },
23114
23115     /**
23116      * Returns the sort state of the Store as an object with two properties:
23117      * <pre><code>
23118  field {String} The name of the field by which the Records are sorted
23119  direction {String} The sort order, "ASC" or "DESC"
23120      * </code></pre>
23121      */
23122     getSortState : function(){
23123         return this.sortInfo;
23124     },
23125
23126     // private
23127     applySort : function(){
23128         if(this.sortInfo && !this.remoteSort){
23129             var s = this.sortInfo, f = s.field;
23130             var st = this.fields.get(f).sortType;
23131             var fn = function(r1, r2){
23132                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23133                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23134             };
23135             this.data.sort(s.direction, fn);
23136             if(this.snapshot && this.snapshot != this.data){
23137                 this.snapshot.sort(s.direction, fn);
23138             }
23139         }
23140     },
23141
23142     /**
23143      * Sets the default sort column and order to be used by the next load operation.
23144      * @param {String} fieldName The name of the field to sort by.
23145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23146      */
23147     setDefaultSort : function(field, dir){
23148         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23149     },
23150
23151     /**
23152      * Sort the Records.
23153      * If remote sorting is used, the sort is performed on the server, and the cache is
23154      * reloaded. If local sorting is used, the cache is sorted internally.
23155      * @param {String} fieldName The name of the field to sort by.
23156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23157      */
23158     sort : function(fieldName, dir){
23159         var f = this.fields.get(fieldName);
23160         if(!dir){
23161             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23162             
23163             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23164                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23165             }else{
23166                 dir = f.sortDir;
23167             }
23168         }
23169         this.sortToggle[f.name] = dir;
23170         this.sortInfo = {field: f.name, direction: dir};
23171         if(!this.remoteSort){
23172             this.applySort();
23173             this.fireEvent("datachanged", this);
23174         }else{
23175             this.load(this.lastOptions);
23176         }
23177     },
23178
23179     /**
23180      * Calls the specified function for each of the Records in the cache.
23181      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23182      * Returning <em>false</em> aborts and exits the iteration.
23183      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23184      */
23185     each : function(fn, scope){
23186         this.data.each(fn, scope);
23187     },
23188
23189     /**
23190      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23191      * (e.g., during paging).
23192      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23193      */
23194     getModifiedRecords : function(){
23195         return this.modified;
23196     },
23197
23198     // private
23199     createFilterFn : function(property, value, anyMatch){
23200         if(!value.exec){ // not a regex
23201             value = String(value);
23202             if(value.length == 0){
23203                 return false;
23204             }
23205             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23206         }
23207         return function(r){
23208             return value.test(r.data[property]);
23209         };
23210     },
23211
23212     /**
23213      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23214      * @param {String} property A field on your records
23215      * @param {Number} start The record index to start at (defaults to 0)
23216      * @param {Number} end The last record index to include (defaults to length - 1)
23217      * @return {Number} The sum
23218      */
23219     sum : function(property, start, end){
23220         var rs = this.data.items, v = 0;
23221         start = start || 0;
23222         end = (end || end === 0) ? end : rs.length-1;
23223
23224         for(var i = start; i <= end; i++){
23225             v += (rs[i].data[property] || 0);
23226         }
23227         return v;
23228     },
23229
23230     /**
23231      * Filter the records by a specified property.
23232      * @param {String} field A field on your records
23233      * @param {String/RegExp} value Either a string that the field
23234      * should start with or a RegExp to test against the field
23235      * @param {Boolean} anyMatch True to match any part not just the beginning
23236      */
23237     filter : function(property, value, anyMatch){
23238         var fn = this.createFilterFn(property, value, anyMatch);
23239         return fn ? this.filterBy(fn) : this.clearFilter();
23240     },
23241
23242     /**
23243      * Filter by a function. The specified function will be called with each
23244      * record in this data source. If the function returns true the record is included,
23245      * otherwise it is filtered.
23246      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23247      * @param {Object} scope (optional) The scope of the function (defaults to this)
23248      */
23249     filterBy : function(fn, scope){
23250         this.snapshot = this.snapshot || this.data;
23251         this.data = this.queryBy(fn, scope||this);
23252         this.fireEvent("datachanged", this);
23253     },
23254
23255     /**
23256      * Query the records by a specified property.
23257      * @param {String} field A field on your records
23258      * @param {String/RegExp} value Either a string that the field
23259      * should start with or a RegExp to test against the field
23260      * @param {Boolean} anyMatch True to match any part not just the beginning
23261      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23262      */
23263     query : function(property, value, anyMatch){
23264         var fn = this.createFilterFn(property, value, anyMatch);
23265         return fn ? this.queryBy(fn) : this.data.clone();
23266     },
23267
23268     /**
23269      * Query by a function. The specified function will be called with each
23270      * record in this data source. If the function returns true the record is included
23271      * in the results.
23272      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23273      * @param {Object} scope (optional) The scope of the function (defaults to this)
23274       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23275      **/
23276     queryBy : function(fn, scope){
23277         var data = this.snapshot || this.data;
23278         return data.filterBy(fn, scope||this);
23279     },
23280
23281     /**
23282      * Collects unique values for a particular dataIndex from this store.
23283      * @param {String} dataIndex The property to collect
23284      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23285      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23286      * @return {Array} An array of the unique values
23287      **/
23288     collect : function(dataIndex, allowNull, bypassFilter){
23289         var d = (bypassFilter === true && this.snapshot) ?
23290                 this.snapshot.items : this.data.items;
23291         var v, sv, r = [], l = {};
23292         for(var i = 0, len = d.length; i < len; i++){
23293             v = d[i].data[dataIndex];
23294             sv = String(v);
23295             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23296                 l[sv] = true;
23297                 r[r.length] = v;
23298             }
23299         }
23300         return r;
23301     },
23302
23303     /**
23304      * Revert to a view of the Record cache with no filtering applied.
23305      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23306      */
23307     clearFilter : function(suppressEvent){
23308         if(this.snapshot && this.snapshot != this.data){
23309             this.data = this.snapshot;
23310             delete this.snapshot;
23311             if(suppressEvent !== true){
23312                 this.fireEvent("datachanged", this);
23313             }
23314         }
23315     },
23316
23317     // private
23318     afterEdit : function(record){
23319         if(this.modified.indexOf(record) == -1){
23320             this.modified.push(record);
23321         }
23322         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23323     },
23324     
23325     // private
23326     afterReject : function(record){
23327         this.modified.remove(record);
23328         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23329     },
23330
23331     // private
23332     afterCommit : function(record){
23333         this.modified.remove(record);
23334         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23335     },
23336
23337     /**
23338      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23339      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23340      */
23341     commitChanges : function(){
23342         var m = this.modified.slice(0);
23343         this.modified = [];
23344         for(var i = 0, len = m.length; i < len; i++){
23345             m[i].commit();
23346         }
23347     },
23348
23349     /**
23350      * Cancel outstanding changes on all changed records.
23351      */
23352     rejectChanges : function(){
23353         var m = this.modified.slice(0);
23354         this.modified = [];
23355         for(var i = 0, len = m.length; i < len; i++){
23356             m[i].reject();
23357         }
23358     },
23359
23360     onMetaChange : function(meta, rtype, o){
23361         this.recordType = rtype;
23362         this.fields = rtype.prototype.fields;
23363         delete this.snapshot;
23364         this.sortInfo = meta.sortInfo || this.sortInfo;
23365         this.modified = [];
23366         this.fireEvent('metachange', this, this.reader.meta);
23367     },
23368     
23369     moveIndex : function(data, type)
23370     {
23371         var index = this.indexOf(data);
23372         
23373         var newIndex = index + type;
23374         
23375         this.remove(data);
23376         
23377         this.insert(newIndex, data);
23378         
23379     }
23380 });/*
23381  * Based on:
23382  * Ext JS Library 1.1.1
23383  * Copyright(c) 2006-2007, Ext JS, LLC.
23384  *
23385  * Originally Released Under LGPL - original licence link has changed is not relivant.
23386  *
23387  * Fork - LGPL
23388  * <script type="text/javascript">
23389  */
23390
23391 /**
23392  * @class Roo.data.SimpleStore
23393  * @extends Roo.data.Store
23394  * Small helper class to make creating Stores from Array data easier.
23395  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23396  * @cfg {Array} fields An array of field definition objects, or field name strings.
23397  * @cfg {Array} data The multi-dimensional array of data
23398  * @constructor
23399  * @param {Object} config
23400  */
23401 Roo.data.SimpleStore = function(config){
23402     Roo.data.SimpleStore.superclass.constructor.call(this, {
23403         isLocal : true,
23404         reader: new Roo.data.ArrayReader({
23405                 id: config.id
23406             },
23407             Roo.data.Record.create(config.fields)
23408         ),
23409         proxy : new Roo.data.MemoryProxy(config.data)
23410     });
23411     this.load();
23412 };
23413 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23414  * Based on:
23415  * Ext JS Library 1.1.1
23416  * Copyright(c) 2006-2007, Ext JS, LLC.
23417  *
23418  * Originally Released Under LGPL - original licence link has changed is not relivant.
23419  *
23420  * Fork - LGPL
23421  * <script type="text/javascript">
23422  */
23423
23424 /**
23425 /**
23426  * @extends Roo.data.Store
23427  * @class Roo.data.JsonStore
23428  * Small helper class to make creating Stores for JSON data easier. <br/>
23429 <pre><code>
23430 var store = new Roo.data.JsonStore({
23431     url: 'get-images.php',
23432     root: 'images',
23433     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23434 });
23435 </code></pre>
23436  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23437  * JsonReader and HttpProxy (unless inline data is provided).</b>
23438  * @cfg {Array} fields An array of field definition objects, or field name strings.
23439  * @constructor
23440  * @param {Object} config
23441  */
23442 Roo.data.JsonStore = function(c){
23443     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23444         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23445         reader: new Roo.data.JsonReader(c, c.fields)
23446     }));
23447 };
23448 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23449  * Based on:
23450  * Ext JS Library 1.1.1
23451  * Copyright(c) 2006-2007, Ext JS, LLC.
23452  *
23453  * Originally Released Under LGPL - original licence link has changed is not relivant.
23454  *
23455  * Fork - LGPL
23456  * <script type="text/javascript">
23457  */
23458
23459  
23460 Roo.data.Field = function(config){
23461     if(typeof config == "string"){
23462         config = {name: config};
23463     }
23464     Roo.apply(this, config);
23465     
23466     if(!this.type){
23467         this.type = "auto";
23468     }
23469     
23470     var st = Roo.data.SortTypes;
23471     // named sortTypes are supported, here we look them up
23472     if(typeof this.sortType == "string"){
23473         this.sortType = st[this.sortType];
23474     }
23475     
23476     // set default sortType for strings and dates
23477     if(!this.sortType){
23478         switch(this.type){
23479             case "string":
23480                 this.sortType = st.asUCString;
23481                 break;
23482             case "date":
23483                 this.sortType = st.asDate;
23484                 break;
23485             default:
23486                 this.sortType = st.none;
23487         }
23488     }
23489
23490     // define once
23491     var stripRe = /[\$,%]/g;
23492
23493     // prebuilt conversion function for this field, instead of
23494     // switching every time we're reading a value
23495     if(!this.convert){
23496         var cv, dateFormat = this.dateFormat;
23497         switch(this.type){
23498             case "":
23499             case "auto":
23500             case undefined:
23501                 cv = function(v){ return v; };
23502                 break;
23503             case "string":
23504                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23505                 break;
23506             case "int":
23507                 cv = function(v){
23508                     return v !== undefined && v !== null && v !== '' ?
23509                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23510                     };
23511                 break;
23512             case "float":
23513                 cv = function(v){
23514                     return v !== undefined && v !== null && v !== '' ?
23515                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23516                     };
23517                 break;
23518             case "bool":
23519             case "boolean":
23520                 cv = function(v){ return v === true || v === "true" || v == 1; };
23521                 break;
23522             case "date":
23523                 cv = function(v){
23524                     if(!v){
23525                         return '';
23526                     }
23527                     if(v instanceof Date){
23528                         return v;
23529                     }
23530                     if(dateFormat){
23531                         if(dateFormat == "timestamp"){
23532                             return new Date(v*1000);
23533                         }
23534                         return Date.parseDate(v, dateFormat);
23535                     }
23536                     var parsed = Date.parse(v);
23537                     return parsed ? new Date(parsed) : null;
23538                 };
23539              break;
23540             
23541         }
23542         this.convert = cv;
23543     }
23544 };
23545
23546 Roo.data.Field.prototype = {
23547     dateFormat: null,
23548     defaultValue: "",
23549     mapping: null,
23550     sortType : null,
23551     sortDir : "ASC"
23552 };/*
23553  * Based on:
23554  * Ext JS Library 1.1.1
23555  * Copyright(c) 2006-2007, Ext JS, LLC.
23556  *
23557  * Originally Released Under LGPL - original licence link has changed is not relivant.
23558  *
23559  * Fork - LGPL
23560  * <script type="text/javascript">
23561  */
23562  
23563 // Base class for reading structured data from a data source.  This class is intended to be
23564 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23565
23566 /**
23567  * @class Roo.data.DataReader
23568  * Base class for reading structured data from a data source.  This class is intended to be
23569  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23570  */
23571
23572 Roo.data.DataReader = function(meta, recordType){
23573     
23574     this.meta = meta;
23575     
23576     this.recordType = recordType instanceof Array ? 
23577         Roo.data.Record.create(recordType) : recordType;
23578 };
23579
23580 Roo.data.DataReader.prototype = {
23581      /**
23582      * Create an empty record
23583      * @param {Object} data (optional) - overlay some values
23584      * @return {Roo.data.Record} record created.
23585      */
23586     newRow :  function(d) {
23587         var da =  {};
23588         this.recordType.prototype.fields.each(function(c) {
23589             switch( c.type) {
23590                 case 'int' : da[c.name] = 0; break;
23591                 case 'date' : da[c.name] = new Date(); break;
23592                 case 'float' : da[c.name] = 0.0; break;
23593                 case 'boolean' : da[c.name] = false; break;
23594                 default : da[c.name] = ""; break;
23595             }
23596             
23597         });
23598         return new this.recordType(Roo.apply(da, d));
23599     }
23600     
23601 };/*
23602  * Based on:
23603  * Ext JS Library 1.1.1
23604  * Copyright(c) 2006-2007, Ext JS, LLC.
23605  *
23606  * Originally Released Under LGPL - original licence link has changed is not relivant.
23607  *
23608  * Fork - LGPL
23609  * <script type="text/javascript">
23610  */
23611
23612 /**
23613  * @class Roo.data.DataProxy
23614  * @extends Roo.data.Observable
23615  * This class is an abstract base class for implementations which provide retrieval of
23616  * unformatted data objects.<br>
23617  * <p>
23618  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23619  * (of the appropriate type which knows how to parse the data object) to provide a block of
23620  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23621  * <p>
23622  * Custom implementations must implement the load method as described in
23623  * {@link Roo.data.HttpProxy#load}.
23624  */
23625 Roo.data.DataProxy = function(){
23626     this.addEvents({
23627         /**
23628          * @event beforeload
23629          * Fires before a network request is made to retrieve a data object.
23630          * @param {Object} This DataProxy object.
23631          * @param {Object} params The params parameter to the load function.
23632          */
23633         beforeload : true,
23634         /**
23635          * @event load
23636          * Fires before the load method's callback is called.
23637          * @param {Object} This DataProxy object.
23638          * @param {Object} o The data object.
23639          * @param {Object} arg The callback argument object passed to the load function.
23640          */
23641         load : true,
23642         /**
23643          * @event loadexception
23644          * Fires if an Exception occurs during data retrieval.
23645          * @param {Object} This DataProxy object.
23646          * @param {Object} o The data object.
23647          * @param {Object} arg The callback argument object passed to the load function.
23648          * @param {Object} e The Exception.
23649          */
23650         loadexception : true
23651     });
23652     Roo.data.DataProxy.superclass.constructor.call(this);
23653 };
23654
23655 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23656
23657     /**
23658      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23659      */
23660 /*
23661  * Based on:
23662  * Ext JS Library 1.1.1
23663  * Copyright(c) 2006-2007, Ext JS, LLC.
23664  *
23665  * Originally Released Under LGPL - original licence link has changed is not relivant.
23666  *
23667  * Fork - LGPL
23668  * <script type="text/javascript">
23669  */
23670 /**
23671  * @class Roo.data.MemoryProxy
23672  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23673  * to the Reader when its load method is called.
23674  * @constructor
23675  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23676  */
23677 Roo.data.MemoryProxy = function(data){
23678     if (data.data) {
23679         data = data.data;
23680     }
23681     Roo.data.MemoryProxy.superclass.constructor.call(this);
23682     this.data = data;
23683 };
23684
23685 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23686     
23687     /**
23688      * Load data from the requested source (in this case an in-memory
23689      * data object passed to the constructor), read the data object into
23690      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23691      * process that block using the passed callback.
23692      * @param {Object} params This parameter is not used by the MemoryProxy class.
23693      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23694      * object into a block of Roo.data.Records.
23695      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23696      * The function must be passed <ul>
23697      * <li>The Record block object</li>
23698      * <li>The "arg" argument from the load function</li>
23699      * <li>A boolean success indicator</li>
23700      * </ul>
23701      * @param {Object} scope The scope in which to call the callback
23702      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23703      */
23704     load : function(params, reader, callback, scope, arg){
23705         params = params || {};
23706         var result;
23707         try {
23708             result = reader.readRecords(this.data);
23709         }catch(e){
23710             this.fireEvent("loadexception", this, arg, null, e);
23711             callback.call(scope, null, arg, false);
23712             return;
23713         }
23714         callback.call(scope, result, arg, true);
23715     },
23716     
23717     // private
23718     update : function(params, records){
23719         
23720     }
23721 });/*
23722  * Based on:
23723  * Ext JS Library 1.1.1
23724  * Copyright(c) 2006-2007, Ext JS, LLC.
23725  *
23726  * Originally Released Under LGPL - original licence link has changed is not relivant.
23727  *
23728  * Fork - LGPL
23729  * <script type="text/javascript">
23730  */
23731 /**
23732  * @class Roo.data.HttpProxy
23733  * @extends Roo.data.DataProxy
23734  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23735  * configured to reference a certain URL.<br><br>
23736  * <p>
23737  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23738  * from which the running page was served.<br><br>
23739  * <p>
23740  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23741  * <p>
23742  * Be aware that to enable the browser to parse an XML document, the server must set
23743  * the Content-Type header in the HTTP response to "text/xml".
23744  * @constructor
23745  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23746  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23747  * will be used to make the request.
23748  */
23749 Roo.data.HttpProxy = function(conn){
23750     Roo.data.HttpProxy.superclass.constructor.call(this);
23751     // is conn a conn config or a real conn?
23752     this.conn = conn;
23753     this.useAjax = !conn || !conn.events;
23754   
23755 };
23756
23757 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23758     // thse are take from connection...
23759     
23760     /**
23761      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23762      */
23763     /**
23764      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23765      * extra parameters to each request made by this object. (defaults to undefined)
23766      */
23767     /**
23768      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23769      *  to each request made by this object. (defaults to undefined)
23770      */
23771     /**
23772      * @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)
23773      */
23774     /**
23775      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23776      */
23777      /**
23778      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23779      * @type Boolean
23780      */
23781   
23782
23783     /**
23784      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23785      * @type Boolean
23786      */
23787     /**
23788      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23789      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23790      * a finer-grained basis than the DataProxy events.
23791      */
23792     getConnection : function(){
23793         return this.useAjax ? Roo.Ajax : this.conn;
23794     },
23795
23796     /**
23797      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23798      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23799      * process that block using the passed callback.
23800      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23801      * for the request to the remote server.
23802      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23803      * object into a block of Roo.data.Records.
23804      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23805      * The function must be passed <ul>
23806      * <li>The Record block object</li>
23807      * <li>The "arg" argument from the load function</li>
23808      * <li>A boolean success indicator</li>
23809      * </ul>
23810      * @param {Object} scope The scope in which to call the callback
23811      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23812      */
23813     load : function(params, reader, callback, scope, arg){
23814         if(this.fireEvent("beforeload", this, params) !== false){
23815             var  o = {
23816                 params : params || {},
23817                 request: {
23818                     callback : callback,
23819                     scope : scope,
23820                     arg : arg
23821                 },
23822                 reader: reader,
23823                 callback : this.loadResponse,
23824                 scope: this
23825             };
23826             if(this.useAjax){
23827                 Roo.applyIf(o, this.conn);
23828                 if(this.activeRequest){
23829                     Roo.Ajax.abort(this.activeRequest);
23830                 }
23831                 this.activeRequest = Roo.Ajax.request(o);
23832             }else{
23833                 this.conn.request(o);
23834             }
23835         }else{
23836             callback.call(scope||this, null, arg, false);
23837         }
23838     },
23839
23840     // private
23841     loadResponse : function(o, success, response){
23842         delete this.activeRequest;
23843         if(!success){
23844             this.fireEvent("loadexception", this, o, response);
23845             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23846             return;
23847         }
23848         var result;
23849         try {
23850             result = o.reader.read(response);
23851         }catch(e){
23852             this.fireEvent("loadexception", this, o, response, e);
23853             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23854             return;
23855         }
23856         
23857         this.fireEvent("load", this, o, o.request.arg);
23858         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23859     },
23860
23861     // private
23862     update : function(dataSet){
23863
23864     },
23865
23866     // private
23867     updateResponse : function(dataSet){
23868
23869     }
23870 });/*
23871  * Based on:
23872  * Ext JS Library 1.1.1
23873  * Copyright(c) 2006-2007, Ext JS, LLC.
23874  *
23875  * Originally Released Under LGPL - original licence link has changed is not relivant.
23876  *
23877  * Fork - LGPL
23878  * <script type="text/javascript">
23879  */
23880
23881 /**
23882  * @class Roo.data.ScriptTagProxy
23883  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23884  * other than the originating domain of the running page.<br><br>
23885  * <p>
23886  * <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
23887  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23888  * <p>
23889  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23890  * source code that is used as the source inside a &lt;script> tag.<br><br>
23891  * <p>
23892  * In order for the browser to process the returned data, the server must wrap the data object
23893  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23894  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23895  * depending on whether the callback name was passed:
23896  * <p>
23897  * <pre><code>
23898 boolean scriptTag = false;
23899 String cb = request.getParameter("callback");
23900 if (cb != null) {
23901     scriptTag = true;
23902     response.setContentType("text/javascript");
23903 } else {
23904     response.setContentType("application/x-json");
23905 }
23906 Writer out = response.getWriter();
23907 if (scriptTag) {
23908     out.write(cb + "(");
23909 }
23910 out.print(dataBlock.toJsonString());
23911 if (scriptTag) {
23912     out.write(");");
23913 }
23914 </pre></code>
23915  *
23916  * @constructor
23917  * @param {Object} config A configuration object.
23918  */
23919 Roo.data.ScriptTagProxy = function(config){
23920     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23921     Roo.apply(this, config);
23922     this.head = document.getElementsByTagName("head")[0];
23923 };
23924
23925 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23926
23927 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23928     /**
23929      * @cfg {String} url The URL from which to request the data object.
23930      */
23931     /**
23932      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23933      */
23934     timeout : 30000,
23935     /**
23936      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23937      * the server the name of the callback function set up by the load call to process the returned data object.
23938      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23939      * javascript output which calls this named function passing the data object as its only parameter.
23940      */
23941     callbackParam : "callback",
23942     /**
23943      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23944      * name to the request.
23945      */
23946     nocache : true,
23947
23948     /**
23949      * Load data from the configured URL, read the data object into
23950      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23951      * process that block using the passed callback.
23952      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23953      * for the request to the remote server.
23954      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23955      * object into a block of Roo.data.Records.
23956      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23957      * The function must be passed <ul>
23958      * <li>The Record block object</li>
23959      * <li>The "arg" argument from the load function</li>
23960      * <li>A boolean success indicator</li>
23961      * </ul>
23962      * @param {Object} scope The scope in which to call the callback
23963      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23964      */
23965     load : function(params, reader, callback, scope, arg){
23966         if(this.fireEvent("beforeload", this, params) !== false){
23967
23968             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23969
23970             var url = this.url;
23971             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23972             if(this.nocache){
23973                 url += "&_dc=" + (new Date().getTime());
23974             }
23975             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23976             var trans = {
23977                 id : transId,
23978                 cb : "stcCallback"+transId,
23979                 scriptId : "stcScript"+transId,
23980                 params : params,
23981                 arg : arg,
23982                 url : url,
23983                 callback : callback,
23984                 scope : scope,
23985                 reader : reader
23986             };
23987             var conn = this;
23988
23989             window[trans.cb] = function(o){
23990                 conn.handleResponse(o, trans);
23991             };
23992
23993             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23994
23995             if(this.autoAbort !== false){
23996                 this.abort();
23997             }
23998
23999             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24000
24001             var script = document.createElement("script");
24002             script.setAttribute("src", url);
24003             script.setAttribute("type", "text/javascript");
24004             script.setAttribute("id", trans.scriptId);
24005             this.head.appendChild(script);
24006
24007             this.trans = trans;
24008         }else{
24009             callback.call(scope||this, null, arg, false);
24010         }
24011     },
24012
24013     // private
24014     isLoading : function(){
24015         return this.trans ? true : false;
24016     },
24017
24018     /**
24019      * Abort the current server request.
24020      */
24021     abort : function(){
24022         if(this.isLoading()){
24023             this.destroyTrans(this.trans);
24024         }
24025     },
24026
24027     // private
24028     destroyTrans : function(trans, isLoaded){
24029         this.head.removeChild(document.getElementById(trans.scriptId));
24030         clearTimeout(trans.timeoutId);
24031         if(isLoaded){
24032             window[trans.cb] = undefined;
24033             try{
24034                 delete window[trans.cb];
24035             }catch(e){}
24036         }else{
24037             // if hasn't been loaded, wait for load to remove it to prevent script error
24038             window[trans.cb] = function(){
24039                 window[trans.cb] = undefined;
24040                 try{
24041                     delete window[trans.cb];
24042                 }catch(e){}
24043             };
24044         }
24045     },
24046
24047     // private
24048     handleResponse : function(o, trans){
24049         this.trans = false;
24050         this.destroyTrans(trans, true);
24051         var result;
24052         try {
24053             result = trans.reader.readRecords(o);
24054         }catch(e){
24055             this.fireEvent("loadexception", this, o, trans.arg, e);
24056             trans.callback.call(trans.scope||window, null, trans.arg, false);
24057             return;
24058         }
24059         this.fireEvent("load", this, o, trans.arg);
24060         trans.callback.call(trans.scope||window, result, trans.arg, true);
24061     },
24062
24063     // private
24064     handleFailure : function(trans){
24065         this.trans = false;
24066         this.destroyTrans(trans, false);
24067         this.fireEvent("loadexception", this, null, trans.arg);
24068         trans.callback.call(trans.scope||window, null, trans.arg, false);
24069     }
24070 });/*
24071  * Based on:
24072  * Ext JS Library 1.1.1
24073  * Copyright(c) 2006-2007, Ext JS, LLC.
24074  *
24075  * Originally Released Under LGPL - original licence link has changed is not relivant.
24076  *
24077  * Fork - LGPL
24078  * <script type="text/javascript">
24079  */
24080
24081 /**
24082  * @class Roo.data.JsonReader
24083  * @extends Roo.data.DataReader
24084  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24085  * based on mappings in a provided Roo.data.Record constructor.
24086  * 
24087  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24088  * in the reply previously. 
24089  * 
24090  * <p>
24091  * Example code:
24092  * <pre><code>
24093 var RecordDef = Roo.data.Record.create([
24094     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24095     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24096 ]);
24097 var myReader = new Roo.data.JsonReader({
24098     totalProperty: "results",    // The property which contains the total dataset size (optional)
24099     root: "rows",                // The property which contains an Array of row objects
24100     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24101 }, RecordDef);
24102 </code></pre>
24103  * <p>
24104  * This would consume a JSON file like this:
24105  * <pre><code>
24106 { 'results': 2, 'rows': [
24107     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24108     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24109 }
24110 </code></pre>
24111  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24112  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24113  * paged from the remote server.
24114  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24115  * @cfg {String} root name of the property which contains the Array of row objects.
24116  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24117  * @cfg {Array} fields Array of field definition objects
24118  * @constructor
24119  * Create a new JsonReader
24120  * @param {Object} meta Metadata configuration options
24121  * @param {Object} recordType Either an Array of field definition objects,
24122  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24123  */
24124 Roo.data.JsonReader = function(meta, recordType){
24125     
24126     meta = meta || {};
24127     // set some defaults:
24128     Roo.applyIf(meta, {
24129         totalProperty: 'total',
24130         successProperty : 'success',
24131         root : 'data',
24132         id : 'id'
24133     });
24134     
24135     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24136 };
24137 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24138     
24139     /**
24140      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24141      * Used by Store query builder to append _requestMeta to params.
24142      * 
24143      */
24144     metaFromRemote : false,
24145     /**
24146      * This method is only used by a DataProxy which has retrieved data from a remote server.
24147      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24148      * @return {Object} data A data block which is used by an Roo.data.Store object as
24149      * a cache of Roo.data.Records.
24150      */
24151     read : function(response){
24152         var json = response.responseText;
24153        
24154         var o = /* eval:var:o */ eval("("+json+")");
24155         if(!o) {
24156             throw {message: "JsonReader.read: Json object not found"};
24157         }
24158         
24159         if(o.metaData){
24160             
24161             delete this.ef;
24162             this.metaFromRemote = true;
24163             this.meta = o.metaData;
24164             this.recordType = Roo.data.Record.create(o.metaData.fields);
24165             this.onMetaChange(this.meta, this.recordType, o);
24166         }
24167         return this.readRecords(o);
24168     },
24169
24170     // private function a store will implement
24171     onMetaChange : function(meta, recordType, o){
24172
24173     },
24174
24175     /**
24176          * @ignore
24177          */
24178     simpleAccess: function(obj, subsc) {
24179         return obj[subsc];
24180     },
24181
24182         /**
24183          * @ignore
24184          */
24185     getJsonAccessor: function(){
24186         var re = /[\[\.]/;
24187         return function(expr) {
24188             try {
24189                 return(re.test(expr))
24190                     ? new Function("obj", "return obj." + expr)
24191                     : function(obj){
24192                         return obj[expr];
24193                     };
24194             } catch(e){}
24195             return Roo.emptyFn;
24196         };
24197     }(),
24198
24199     /**
24200      * Create a data block containing Roo.data.Records from an XML document.
24201      * @param {Object} o An object which contains an Array of row objects in the property specified
24202      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24203      * which contains the total size of the dataset.
24204      * @return {Object} data A data block which is used by an Roo.data.Store object as
24205      * a cache of Roo.data.Records.
24206      */
24207     readRecords : function(o){
24208         /**
24209          * After any data loads, the raw JSON data is available for further custom processing.
24210          * @type Object
24211          */
24212         this.o = o;
24213         var s = this.meta, Record = this.recordType,
24214             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24215
24216 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24217         if (!this.ef) {
24218             if(s.totalProperty) {
24219                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24220                 }
24221                 if(s.successProperty) {
24222                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24223                 }
24224                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24225                 if (s.id) {
24226                         var g = this.getJsonAccessor(s.id);
24227                         this.getId = function(rec) {
24228                                 var r = g(rec);  
24229                                 return (r === undefined || r === "") ? null : r;
24230                         };
24231                 } else {
24232                         this.getId = function(){return null;};
24233                 }
24234             this.ef = [];
24235             for(var jj = 0; jj < fl; jj++){
24236                 f = fi[jj];
24237                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24238                 this.ef[jj] = this.getJsonAccessor(map);
24239             }
24240         }
24241
24242         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24243         if(s.totalProperty){
24244             var vt = parseInt(this.getTotal(o), 10);
24245             if(!isNaN(vt)){
24246                 totalRecords = vt;
24247             }
24248         }
24249         if(s.successProperty){
24250             var vs = this.getSuccess(o);
24251             if(vs === false || vs === 'false'){
24252                 success = false;
24253             }
24254         }
24255         var records = [];
24256         for(var i = 0; i < c; i++){
24257                 var n = root[i];
24258             var values = {};
24259             var id = this.getId(n);
24260             for(var j = 0; j < fl; j++){
24261                 f = fi[j];
24262             var v = this.ef[j](n);
24263             if (!f.convert) {
24264                 Roo.log('missing convert for ' + f.name);
24265                 Roo.log(f);
24266                 continue;
24267             }
24268             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24269             }
24270             var record = new Record(values, id);
24271             record.json = n;
24272             records[i] = record;
24273         }
24274         return {
24275             raw : o,
24276             success : success,
24277             records : records,
24278             totalRecords : totalRecords
24279         };
24280     }
24281 });/*
24282  * Based on:
24283  * Ext JS Library 1.1.1
24284  * Copyright(c) 2006-2007, Ext JS, LLC.
24285  *
24286  * Originally Released Under LGPL - original licence link has changed is not relivant.
24287  *
24288  * Fork - LGPL
24289  * <script type="text/javascript">
24290  */
24291
24292 /**
24293  * @class Roo.data.XmlReader
24294  * @extends Roo.data.DataReader
24295  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24296  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24297  * <p>
24298  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24299  * header in the HTTP response must be set to "text/xml".</em>
24300  * <p>
24301  * Example code:
24302  * <pre><code>
24303 var RecordDef = Roo.data.Record.create([
24304    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24305    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24306 ]);
24307 var myReader = new Roo.data.XmlReader({
24308    totalRecords: "results", // The element which contains the total dataset size (optional)
24309    record: "row",           // The repeated element which contains row information
24310    id: "id"                 // The element within the row that provides an ID for the record (optional)
24311 }, RecordDef);
24312 </code></pre>
24313  * <p>
24314  * This would consume an XML file like this:
24315  * <pre><code>
24316 &lt;?xml?>
24317 &lt;dataset>
24318  &lt;results>2&lt;/results>
24319  &lt;row>
24320    &lt;id>1&lt;/id>
24321    &lt;name>Bill&lt;/name>
24322    &lt;occupation>Gardener&lt;/occupation>
24323  &lt;/row>
24324  &lt;row>
24325    &lt;id>2&lt;/id>
24326    &lt;name>Ben&lt;/name>
24327    &lt;occupation>Horticulturalist&lt;/occupation>
24328  &lt;/row>
24329 &lt;/dataset>
24330 </code></pre>
24331  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24332  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24333  * paged from the remote server.
24334  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24335  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24336  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24337  * a record identifier value.
24338  * @constructor
24339  * Create a new XmlReader
24340  * @param {Object} meta Metadata configuration options
24341  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24342  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24343  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24344  */
24345 Roo.data.XmlReader = function(meta, recordType){
24346     meta = meta || {};
24347     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24348 };
24349 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24350     /**
24351      * This method is only used by a DataProxy which has retrieved data from a remote server.
24352          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24353          * to contain a method called 'responseXML' that returns an XML document object.
24354      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24355      * a cache of Roo.data.Records.
24356      */
24357     read : function(response){
24358         var doc = response.responseXML;
24359         if(!doc) {
24360             throw {message: "XmlReader.read: XML Document not available"};
24361         }
24362         return this.readRecords(doc);
24363     },
24364
24365     /**
24366      * Create a data block containing Roo.data.Records from an XML document.
24367          * @param {Object} doc A parsed XML document.
24368      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24369      * a cache of Roo.data.Records.
24370      */
24371     readRecords : function(doc){
24372         /**
24373          * After any data loads/reads, the raw XML Document is available for further custom processing.
24374          * @type XMLDocument
24375          */
24376         this.xmlData = doc;
24377         var root = doc.documentElement || doc;
24378         var q = Roo.DomQuery;
24379         var recordType = this.recordType, fields = recordType.prototype.fields;
24380         var sid = this.meta.id;
24381         var totalRecords = 0, success = true;
24382         if(this.meta.totalRecords){
24383             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24384         }
24385         
24386         if(this.meta.success){
24387             var sv = q.selectValue(this.meta.success, root, true);
24388             success = sv !== false && sv !== 'false';
24389         }
24390         var records = [];
24391         var ns = q.select(this.meta.record, root);
24392         for(var i = 0, len = ns.length; i < len; i++) {
24393                 var n = ns[i];
24394                 var values = {};
24395                 var id = sid ? q.selectValue(sid, n) : undefined;
24396                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24397                     var f = fields.items[j];
24398                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24399                     v = f.convert(v);
24400                     values[f.name] = v;
24401                 }
24402                 var record = new recordType(values, id);
24403                 record.node = n;
24404                 records[records.length] = record;
24405             }
24406
24407             return {
24408                 success : success,
24409                 records : records,
24410                 totalRecords : totalRecords || records.length
24411             };
24412     }
24413 });/*
24414  * Based on:
24415  * Ext JS Library 1.1.1
24416  * Copyright(c) 2006-2007, Ext JS, LLC.
24417  *
24418  * Originally Released Under LGPL - original licence link has changed is not relivant.
24419  *
24420  * Fork - LGPL
24421  * <script type="text/javascript">
24422  */
24423
24424 /**
24425  * @class Roo.data.ArrayReader
24426  * @extends Roo.data.DataReader
24427  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24428  * Each element of that Array represents a row of data fields. The
24429  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24430  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24431  * <p>
24432  * Example code:.
24433  * <pre><code>
24434 var RecordDef = Roo.data.Record.create([
24435     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24436     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24437 ]);
24438 var myReader = new Roo.data.ArrayReader({
24439     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24440 }, RecordDef);
24441 </code></pre>
24442  * <p>
24443  * This would consume an Array like this:
24444  * <pre><code>
24445 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24446   </code></pre>
24447  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24448  * @constructor
24449  * Create a new JsonReader
24450  * @param {Object} meta Metadata configuration options.
24451  * @param {Object} recordType Either an Array of field definition objects
24452  * as specified to {@link Roo.data.Record#create},
24453  * or an {@link Roo.data.Record} object
24454  * created using {@link Roo.data.Record#create}.
24455  */
24456 Roo.data.ArrayReader = function(meta, recordType){
24457     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24458 };
24459
24460 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24461     /**
24462      * Create a data block containing Roo.data.Records from an XML document.
24463      * @param {Object} o An Array of row objects which represents the dataset.
24464      * @return {Object} data A data block which is used by an Roo.data.Store object as
24465      * a cache of Roo.data.Records.
24466      */
24467     readRecords : function(o){
24468         var sid = this.meta ? this.meta.id : null;
24469         var recordType = this.recordType, fields = recordType.prototype.fields;
24470         var records = [];
24471         var root = o;
24472             for(var i = 0; i < root.length; i++){
24473                     var n = root[i];
24474                 var values = {};
24475                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24476                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24477                 var f = fields.items[j];
24478                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24479                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24480                 v = f.convert(v);
24481                 values[f.name] = v;
24482             }
24483                 var record = new recordType(values, id);
24484                 record.json = n;
24485                 records[records.length] = record;
24486             }
24487             return {
24488                 records : records,
24489                 totalRecords : records.length
24490             };
24491     }
24492 });/*
24493  * Based on:
24494  * Ext JS Library 1.1.1
24495  * Copyright(c) 2006-2007, Ext JS, LLC.
24496  *
24497  * Originally Released Under LGPL - original licence link has changed is not relivant.
24498  *
24499  * Fork - LGPL
24500  * <script type="text/javascript">
24501  */
24502
24503
24504 /**
24505  * @class Roo.data.Tree
24506  * @extends Roo.util.Observable
24507  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24508  * in the tree have most standard DOM functionality.
24509  * @constructor
24510  * @param {Node} root (optional) The root node
24511  */
24512 Roo.data.Tree = function(root){
24513    this.nodeHash = {};
24514    /**
24515     * The root node for this tree
24516     * @type Node
24517     */
24518    this.root = null;
24519    if(root){
24520        this.setRootNode(root);
24521    }
24522    this.addEvents({
24523        /**
24524         * @event append
24525         * Fires when a new child node is appended to a node in this tree.
24526         * @param {Tree} tree The owner tree
24527         * @param {Node} parent The parent node
24528         * @param {Node} node The newly appended node
24529         * @param {Number} index The index of the newly appended node
24530         */
24531        "append" : true,
24532        /**
24533         * @event remove
24534         * Fires when a child node is removed from a node in this tree.
24535         * @param {Tree} tree The owner tree
24536         * @param {Node} parent The parent node
24537         * @param {Node} node The child node removed
24538         */
24539        "remove" : true,
24540        /**
24541         * @event move
24542         * Fires when a node is moved to a new location in the tree
24543         * @param {Tree} tree The owner tree
24544         * @param {Node} node The node moved
24545         * @param {Node} oldParent The old parent of this node
24546         * @param {Node} newParent The new parent of this node
24547         * @param {Number} index The index it was moved to
24548         */
24549        "move" : true,
24550        /**
24551         * @event insert
24552         * Fires when a new child node is inserted in a node in this tree.
24553         * @param {Tree} tree The owner tree
24554         * @param {Node} parent The parent node
24555         * @param {Node} node The child node inserted
24556         * @param {Node} refNode The child node the node was inserted before
24557         */
24558        "insert" : true,
24559        /**
24560         * @event beforeappend
24561         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24562         * @param {Tree} tree The owner tree
24563         * @param {Node} parent The parent node
24564         * @param {Node} node The child node to be appended
24565         */
24566        "beforeappend" : true,
24567        /**
24568         * @event beforeremove
24569         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24570         * @param {Tree} tree The owner tree
24571         * @param {Node} parent The parent node
24572         * @param {Node} node The child node to be removed
24573         */
24574        "beforeremove" : true,
24575        /**
24576         * @event beforemove
24577         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24578         * @param {Tree} tree The owner tree
24579         * @param {Node} node The node being moved
24580         * @param {Node} oldParent The parent of the node
24581         * @param {Node} newParent The new parent the node is moving to
24582         * @param {Number} index The index it is being moved to
24583         */
24584        "beforemove" : true,
24585        /**
24586         * @event beforeinsert
24587         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24588         * @param {Tree} tree The owner tree
24589         * @param {Node} parent The parent node
24590         * @param {Node} node The child node to be inserted
24591         * @param {Node} refNode The child node the node is being inserted before
24592         */
24593        "beforeinsert" : true
24594    });
24595
24596     Roo.data.Tree.superclass.constructor.call(this);
24597 };
24598
24599 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24600     pathSeparator: "/",
24601
24602     proxyNodeEvent : function(){
24603         return this.fireEvent.apply(this, arguments);
24604     },
24605
24606     /**
24607      * Returns the root node for this tree.
24608      * @return {Node}
24609      */
24610     getRootNode : function(){
24611         return this.root;
24612     },
24613
24614     /**
24615      * Sets the root node for this tree.
24616      * @param {Node} node
24617      * @return {Node}
24618      */
24619     setRootNode : function(node){
24620         this.root = node;
24621         node.ownerTree = this;
24622         node.isRoot = true;
24623         this.registerNode(node);
24624         return node;
24625     },
24626
24627     /**
24628      * Gets a node in this tree by its id.
24629      * @param {String} id
24630      * @return {Node}
24631      */
24632     getNodeById : function(id){
24633         return this.nodeHash[id];
24634     },
24635
24636     registerNode : function(node){
24637         this.nodeHash[node.id] = node;
24638     },
24639
24640     unregisterNode : function(node){
24641         delete this.nodeHash[node.id];
24642     },
24643
24644     toString : function(){
24645         return "[Tree"+(this.id?" "+this.id:"")+"]";
24646     }
24647 });
24648
24649 /**
24650  * @class Roo.data.Node
24651  * @extends Roo.util.Observable
24652  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24653  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24654  * @constructor
24655  * @param {Object} attributes The attributes/config for the node
24656  */
24657 Roo.data.Node = function(attributes){
24658     /**
24659      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24660      * @type {Object}
24661      */
24662     this.attributes = attributes || {};
24663     this.leaf = this.attributes.leaf;
24664     /**
24665      * The node id. @type String
24666      */
24667     this.id = this.attributes.id;
24668     if(!this.id){
24669         this.id = Roo.id(null, "ynode-");
24670         this.attributes.id = this.id;
24671     }
24672      
24673     
24674     /**
24675      * All child nodes of this node. @type Array
24676      */
24677     this.childNodes = [];
24678     if(!this.childNodes.indexOf){ // indexOf is a must
24679         this.childNodes.indexOf = function(o){
24680             for(var i = 0, len = this.length; i < len; i++){
24681                 if(this[i] == o) {
24682                     return i;
24683                 }
24684             }
24685             return -1;
24686         };
24687     }
24688     /**
24689      * The parent node for this node. @type Node
24690      */
24691     this.parentNode = null;
24692     /**
24693      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24694      */
24695     this.firstChild = null;
24696     /**
24697      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24698      */
24699     this.lastChild = null;
24700     /**
24701      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24702      */
24703     this.previousSibling = null;
24704     /**
24705      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24706      */
24707     this.nextSibling = null;
24708
24709     this.addEvents({
24710        /**
24711         * @event append
24712         * Fires when a new child node is appended
24713         * @param {Tree} tree The owner tree
24714         * @param {Node} this This node
24715         * @param {Node} node The newly appended node
24716         * @param {Number} index The index of the newly appended node
24717         */
24718        "append" : true,
24719        /**
24720         * @event remove
24721         * Fires when a child node is removed
24722         * @param {Tree} tree The owner tree
24723         * @param {Node} this This node
24724         * @param {Node} node The removed node
24725         */
24726        "remove" : true,
24727        /**
24728         * @event move
24729         * Fires when this node is moved to a new location in the tree
24730         * @param {Tree} tree The owner tree
24731         * @param {Node} this This node
24732         * @param {Node} oldParent The old parent of this node
24733         * @param {Node} newParent The new parent of this node
24734         * @param {Number} index The index it was moved to
24735         */
24736        "move" : true,
24737        /**
24738         * @event insert
24739         * Fires when a new child node is inserted.
24740         * @param {Tree} tree The owner tree
24741         * @param {Node} this This node
24742         * @param {Node} node The child node inserted
24743         * @param {Node} refNode The child node the node was inserted before
24744         */
24745        "insert" : true,
24746        /**
24747         * @event beforeappend
24748         * Fires before a new child is appended, return false to cancel the append.
24749         * @param {Tree} tree The owner tree
24750         * @param {Node} this This node
24751         * @param {Node} node The child node to be appended
24752         */
24753        "beforeappend" : true,
24754        /**
24755         * @event beforeremove
24756         * Fires before a child is removed, return false to cancel the remove.
24757         * @param {Tree} tree The owner tree
24758         * @param {Node} this This node
24759         * @param {Node} node The child node to be removed
24760         */
24761        "beforeremove" : true,
24762        /**
24763         * @event beforemove
24764         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24765         * @param {Tree} tree The owner tree
24766         * @param {Node} this This node
24767         * @param {Node} oldParent The parent of this node
24768         * @param {Node} newParent The new parent this node is moving to
24769         * @param {Number} index The index it is being moved to
24770         */
24771        "beforemove" : true,
24772        /**
24773         * @event beforeinsert
24774         * Fires before a new child is inserted, return false to cancel the insert.
24775         * @param {Tree} tree The owner tree
24776         * @param {Node} this This node
24777         * @param {Node} node The child node to be inserted
24778         * @param {Node} refNode The child node the node is being inserted before
24779         */
24780        "beforeinsert" : true
24781    });
24782     this.listeners = this.attributes.listeners;
24783     Roo.data.Node.superclass.constructor.call(this);
24784 };
24785
24786 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24787     fireEvent : function(evtName){
24788         // first do standard event for this node
24789         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24790             return false;
24791         }
24792         // then bubble it up to the tree if the event wasn't cancelled
24793         var ot = this.getOwnerTree();
24794         if(ot){
24795             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24796                 return false;
24797             }
24798         }
24799         return true;
24800     },
24801
24802     /**
24803      * Returns true if this node is a leaf
24804      * @return {Boolean}
24805      */
24806     isLeaf : function(){
24807         return this.leaf === true;
24808     },
24809
24810     // private
24811     setFirstChild : function(node){
24812         this.firstChild = node;
24813     },
24814
24815     //private
24816     setLastChild : function(node){
24817         this.lastChild = node;
24818     },
24819
24820
24821     /**
24822      * Returns true if this node is the last child of its parent
24823      * @return {Boolean}
24824      */
24825     isLast : function(){
24826        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24827     },
24828
24829     /**
24830      * Returns true if this node is the first child of its parent
24831      * @return {Boolean}
24832      */
24833     isFirst : function(){
24834        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24835     },
24836
24837     hasChildNodes : function(){
24838         return !this.isLeaf() && this.childNodes.length > 0;
24839     },
24840
24841     /**
24842      * Insert node(s) as the last child node of this node.
24843      * @param {Node/Array} node The node or Array of nodes to append
24844      * @return {Node} The appended node if single append, or null if an array was passed
24845      */
24846     appendChild : function(node){
24847         var multi = false;
24848         if(node instanceof Array){
24849             multi = node;
24850         }else if(arguments.length > 1){
24851             multi = arguments;
24852         }
24853         // if passed an array or multiple args do them one by one
24854         if(multi){
24855             for(var i = 0, len = multi.length; i < len; i++) {
24856                 this.appendChild(multi[i]);
24857             }
24858         }else{
24859             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24860                 return false;
24861             }
24862             var index = this.childNodes.length;
24863             var oldParent = node.parentNode;
24864             // it's a move, make sure we move it cleanly
24865             if(oldParent){
24866                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24867                     return false;
24868                 }
24869                 oldParent.removeChild(node);
24870             }
24871             index = this.childNodes.length;
24872             if(index == 0){
24873                 this.setFirstChild(node);
24874             }
24875             this.childNodes.push(node);
24876             node.parentNode = this;
24877             var ps = this.childNodes[index-1];
24878             if(ps){
24879                 node.previousSibling = ps;
24880                 ps.nextSibling = node;
24881             }else{
24882                 node.previousSibling = null;
24883             }
24884             node.nextSibling = null;
24885             this.setLastChild(node);
24886             node.setOwnerTree(this.getOwnerTree());
24887             this.fireEvent("append", this.ownerTree, this, node, index);
24888             if(oldParent){
24889                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24890             }
24891             return node;
24892         }
24893     },
24894
24895     /**
24896      * Removes a child node from this node.
24897      * @param {Node} node The node to remove
24898      * @return {Node} The removed node
24899      */
24900     removeChild : function(node){
24901         var index = this.childNodes.indexOf(node);
24902         if(index == -1){
24903             return false;
24904         }
24905         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24906             return false;
24907         }
24908
24909         // remove it from childNodes collection
24910         this.childNodes.splice(index, 1);
24911
24912         // update siblings
24913         if(node.previousSibling){
24914             node.previousSibling.nextSibling = node.nextSibling;
24915         }
24916         if(node.nextSibling){
24917             node.nextSibling.previousSibling = node.previousSibling;
24918         }
24919
24920         // update child refs
24921         if(this.firstChild == node){
24922             this.setFirstChild(node.nextSibling);
24923         }
24924         if(this.lastChild == node){
24925             this.setLastChild(node.previousSibling);
24926         }
24927
24928         node.setOwnerTree(null);
24929         // clear any references from the node
24930         node.parentNode = null;
24931         node.previousSibling = null;
24932         node.nextSibling = null;
24933         this.fireEvent("remove", this.ownerTree, this, node);
24934         return node;
24935     },
24936
24937     /**
24938      * Inserts the first node before the second node in this nodes childNodes collection.
24939      * @param {Node} node The node to insert
24940      * @param {Node} refNode The node to insert before (if null the node is appended)
24941      * @return {Node} The inserted node
24942      */
24943     insertBefore : function(node, refNode){
24944         if(!refNode){ // like standard Dom, refNode can be null for append
24945             return this.appendChild(node);
24946         }
24947         // nothing to do
24948         if(node == refNode){
24949             return false;
24950         }
24951
24952         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24953             return false;
24954         }
24955         var index = this.childNodes.indexOf(refNode);
24956         var oldParent = node.parentNode;
24957         var refIndex = index;
24958
24959         // when moving internally, indexes will change after remove
24960         if(oldParent == this && this.childNodes.indexOf(node) < index){
24961             refIndex--;
24962         }
24963
24964         // it's a move, make sure we move it cleanly
24965         if(oldParent){
24966             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24967                 return false;
24968             }
24969             oldParent.removeChild(node);
24970         }
24971         if(refIndex == 0){
24972             this.setFirstChild(node);
24973         }
24974         this.childNodes.splice(refIndex, 0, node);
24975         node.parentNode = this;
24976         var ps = this.childNodes[refIndex-1];
24977         if(ps){
24978             node.previousSibling = ps;
24979             ps.nextSibling = node;
24980         }else{
24981             node.previousSibling = null;
24982         }
24983         node.nextSibling = refNode;
24984         refNode.previousSibling = node;
24985         node.setOwnerTree(this.getOwnerTree());
24986         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24987         if(oldParent){
24988             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24989         }
24990         return node;
24991     },
24992
24993     /**
24994      * Returns the child node at the specified index.
24995      * @param {Number} index
24996      * @return {Node}
24997      */
24998     item : function(index){
24999         return this.childNodes[index];
25000     },
25001
25002     /**
25003      * Replaces one child node in this node with another.
25004      * @param {Node} newChild The replacement node
25005      * @param {Node} oldChild The node to replace
25006      * @return {Node} The replaced node
25007      */
25008     replaceChild : function(newChild, oldChild){
25009         this.insertBefore(newChild, oldChild);
25010         this.removeChild(oldChild);
25011         return oldChild;
25012     },
25013
25014     /**
25015      * Returns the index of a child node
25016      * @param {Node} node
25017      * @return {Number} The index of the node or -1 if it was not found
25018      */
25019     indexOf : function(child){
25020         return this.childNodes.indexOf(child);
25021     },
25022
25023     /**
25024      * Returns the tree this node is in.
25025      * @return {Tree}
25026      */
25027     getOwnerTree : function(){
25028         // if it doesn't have one, look for one
25029         if(!this.ownerTree){
25030             var p = this;
25031             while(p){
25032                 if(p.ownerTree){
25033                     this.ownerTree = p.ownerTree;
25034                     break;
25035                 }
25036                 p = p.parentNode;
25037             }
25038         }
25039         return this.ownerTree;
25040     },
25041
25042     /**
25043      * Returns depth of this node (the root node has a depth of 0)
25044      * @return {Number}
25045      */
25046     getDepth : function(){
25047         var depth = 0;
25048         var p = this;
25049         while(p.parentNode){
25050             ++depth;
25051             p = p.parentNode;
25052         }
25053         return depth;
25054     },
25055
25056     // private
25057     setOwnerTree : function(tree){
25058         // if it's move, we need to update everyone
25059         if(tree != this.ownerTree){
25060             if(this.ownerTree){
25061                 this.ownerTree.unregisterNode(this);
25062             }
25063             this.ownerTree = tree;
25064             var cs = this.childNodes;
25065             for(var i = 0, len = cs.length; i < len; i++) {
25066                 cs[i].setOwnerTree(tree);
25067             }
25068             if(tree){
25069                 tree.registerNode(this);
25070             }
25071         }
25072     },
25073
25074     /**
25075      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25076      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25077      * @return {String} The path
25078      */
25079     getPath : function(attr){
25080         attr = attr || "id";
25081         var p = this.parentNode;
25082         var b = [this.attributes[attr]];
25083         while(p){
25084             b.unshift(p.attributes[attr]);
25085             p = p.parentNode;
25086         }
25087         var sep = this.getOwnerTree().pathSeparator;
25088         return sep + b.join(sep);
25089     },
25090
25091     /**
25092      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25093      * function call will be the scope provided or the current node. The arguments to the function
25094      * will be the args provided or the current node. If the function returns false at any point,
25095      * the bubble is stopped.
25096      * @param {Function} fn The function to call
25097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25099      */
25100     bubble : function(fn, scope, args){
25101         var p = this;
25102         while(p){
25103             if(fn.call(scope || p, args || p) === false){
25104                 break;
25105             }
25106             p = p.parentNode;
25107         }
25108     },
25109
25110     /**
25111      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25112      * function call will be the scope provided or the current node. The arguments to the function
25113      * will be the args provided or the current node. If the function returns false at any point,
25114      * the cascade is stopped on that branch.
25115      * @param {Function} fn The function to call
25116      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25117      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25118      */
25119     cascade : function(fn, scope, args){
25120         if(fn.call(scope || this, args || this) !== false){
25121             var cs = this.childNodes;
25122             for(var i = 0, len = cs.length; i < len; i++) {
25123                 cs[i].cascade(fn, scope, args);
25124             }
25125         }
25126     },
25127
25128     /**
25129      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25130      * function call will be the scope provided or the current node. The arguments to the function
25131      * will be the args provided or the current node. If the function returns false at any point,
25132      * the iteration stops.
25133      * @param {Function} fn The function to call
25134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25136      */
25137     eachChild : function(fn, scope, args){
25138         var cs = this.childNodes;
25139         for(var i = 0, len = cs.length; i < len; i++) {
25140                 if(fn.call(scope || this, args || cs[i]) === false){
25141                     break;
25142                 }
25143         }
25144     },
25145
25146     /**
25147      * Finds the first child that has the attribute with the specified value.
25148      * @param {String} attribute The attribute name
25149      * @param {Mixed} value The value to search for
25150      * @return {Node} The found child or null if none was found
25151      */
25152     findChild : function(attribute, value){
25153         var cs = this.childNodes;
25154         for(var i = 0, len = cs.length; i < len; i++) {
25155                 if(cs[i].attributes[attribute] == value){
25156                     return cs[i];
25157                 }
25158         }
25159         return null;
25160     },
25161
25162     /**
25163      * Finds the first child by a custom function. The child matches if the function passed
25164      * returns true.
25165      * @param {Function} fn
25166      * @param {Object} scope (optional)
25167      * @return {Node} The found child or null if none was found
25168      */
25169     findChildBy : function(fn, scope){
25170         var cs = this.childNodes;
25171         for(var i = 0, len = cs.length; i < len; i++) {
25172                 if(fn.call(scope||cs[i], cs[i]) === true){
25173                     return cs[i];
25174                 }
25175         }
25176         return null;
25177     },
25178
25179     /**
25180      * Sorts this nodes children using the supplied sort function
25181      * @param {Function} fn
25182      * @param {Object} scope (optional)
25183      */
25184     sort : function(fn, scope){
25185         var cs = this.childNodes;
25186         var len = cs.length;
25187         if(len > 0){
25188             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25189             cs.sort(sortFn);
25190             for(var i = 0; i < len; i++){
25191                 var n = cs[i];
25192                 n.previousSibling = cs[i-1];
25193                 n.nextSibling = cs[i+1];
25194                 if(i == 0){
25195                     this.setFirstChild(n);
25196                 }
25197                 if(i == len-1){
25198                     this.setLastChild(n);
25199                 }
25200             }
25201         }
25202     },
25203
25204     /**
25205      * Returns true if this node is an ancestor (at any point) of the passed node.
25206      * @param {Node} node
25207      * @return {Boolean}
25208      */
25209     contains : function(node){
25210         return node.isAncestor(this);
25211     },
25212
25213     /**
25214      * Returns true if the passed node is an ancestor (at any point) of this node.
25215      * @param {Node} node
25216      * @return {Boolean}
25217      */
25218     isAncestor : function(node){
25219         var p = this.parentNode;
25220         while(p){
25221             if(p == node){
25222                 return true;
25223             }
25224             p = p.parentNode;
25225         }
25226         return false;
25227     },
25228
25229     toString : function(){
25230         return "[Node"+(this.id?" "+this.id:"")+"]";
25231     }
25232 });/*
25233  * Based on:
25234  * Ext JS Library 1.1.1
25235  * Copyright(c) 2006-2007, Ext JS, LLC.
25236  *
25237  * Originally Released Under LGPL - original licence link has changed is not relivant.
25238  *
25239  * Fork - LGPL
25240  * <script type="text/javascript">
25241  */
25242  (function(){ 
25243 /**
25244  * @class Roo.Layer
25245  * @extends Roo.Element
25246  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25247  * automatic maintaining of shadow/shim positions.
25248  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25249  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25250  * you can pass a string with a CSS class name. False turns off the shadow.
25251  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25252  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25253  * @cfg {String} cls CSS class to add to the element
25254  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25255  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25256  * @constructor
25257  * @param {Object} config An object with config options.
25258  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25259  */
25260
25261 Roo.Layer = function(config, existingEl){
25262     config = config || {};
25263     var dh = Roo.DomHelper;
25264     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25265     if(existingEl){
25266         this.dom = Roo.getDom(existingEl);
25267     }
25268     if(!this.dom){
25269         var o = config.dh || {tag: "div", cls: "x-layer"};
25270         this.dom = dh.append(pel, o);
25271     }
25272     if(config.cls){
25273         this.addClass(config.cls);
25274     }
25275     this.constrain = config.constrain !== false;
25276     this.visibilityMode = Roo.Element.VISIBILITY;
25277     if(config.id){
25278         this.id = this.dom.id = config.id;
25279     }else{
25280         this.id = Roo.id(this.dom);
25281     }
25282     this.zindex = config.zindex || this.getZIndex();
25283     this.position("absolute", this.zindex);
25284     if(config.shadow){
25285         this.shadowOffset = config.shadowOffset || 4;
25286         this.shadow = new Roo.Shadow({
25287             offset : this.shadowOffset,
25288             mode : config.shadow
25289         });
25290     }else{
25291         this.shadowOffset = 0;
25292     }
25293     this.useShim = config.shim !== false && Roo.useShims;
25294     this.useDisplay = config.useDisplay;
25295     this.hide();
25296 };
25297
25298 var supr = Roo.Element.prototype;
25299
25300 // shims are shared among layer to keep from having 100 iframes
25301 var shims = [];
25302
25303 Roo.extend(Roo.Layer, Roo.Element, {
25304
25305     getZIndex : function(){
25306         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25307     },
25308
25309     getShim : function(){
25310         if(!this.useShim){
25311             return null;
25312         }
25313         if(this.shim){
25314             return this.shim;
25315         }
25316         var shim = shims.shift();
25317         if(!shim){
25318             shim = this.createShim();
25319             shim.enableDisplayMode('block');
25320             shim.dom.style.display = 'none';
25321             shim.dom.style.visibility = 'visible';
25322         }
25323         var pn = this.dom.parentNode;
25324         if(shim.dom.parentNode != pn){
25325             pn.insertBefore(shim.dom, this.dom);
25326         }
25327         shim.setStyle('z-index', this.getZIndex()-2);
25328         this.shim = shim;
25329         return shim;
25330     },
25331
25332     hideShim : function(){
25333         if(this.shim){
25334             this.shim.setDisplayed(false);
25335             shims.push(this.shim);
25336             delete this.shim;
25337         }
25338     },
25339
25340     disableShadow : function(){
25341         if(this.shadow){
25342             this.shadowDisabled = true;
25343             this.shadow.hide();
25344             this.lastShadowOffset = this.shadowOffset;
25345             this.shadowOffset = 0;
25346         }
25347     },
25348
25349     enableShadow : function(show){
25350         if(this.shadow){
25351             this.shadowDisabled = false;
25352             this.shadowOffset = this.lastShadowOffset;
25353             delete this.lastShadowOffset;
25354             if(show){
25355                 this.sync(true);
25356             }
25357         }
25358     },
25359
25360     // private
25361     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25362     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25363     sync : function(doShow){
25364         var sw = this.shadow;
25365         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25366             var sh = this.getShim();
25367
25368             var w = this.getWidth(),
25369                 h = this.getHeight();
25370
25371             var l = this.getLeft(true),
25372                 t = this.getTop(true);
25373
25374             if(sw && !this.shadowDisabled){
25375                 if(doShow && !sw.isVisible()){
25376                     sw.show(this);
25377                 }else{
25378                     sw.realign(l, t, w, h);
25379                 }
25380                 if(sh){
25381                     if(doShow){
25382                        sh.show();
25383                     }
25384                     // fit the shim behind the shadow, so it is shimmed too
25385                     var a = sw.adjusts, s = sh.dom.style;
25386                     s.left = (Math.min(l, l+a.l))+"px";
25387                     s.top = (Math.min(t, t+a.t))+"px";
25388                     s.width = (w+a.w)+"px";
25389                     s.height = (h+a.h)+"px";
25390                 }
25391             }else if(sh){
25392                 if(doShow){
25393                    sh.show();
25394                 }
25395                 sh.setSize(w, h);
25396                 sh.setLeftTop(l, t);
25397             }
25398             
25399         }
25400     },
25401
25402     // private
25403     destroy : function(){
25404         this.hideShim();
25405         if(this.shadow){
25406             this.shadow.hide();
25407         }
25408         this.removeAllListeners();
25409         var pn = this.dom.parentNode;
25410         if(pn){
25411             pn.removeChild(this.dom);
25412         }
25413         Roo.Element.uncache(this.id);
25414     },
25415
25416     remove : function(){
25417         this.destroy();
25418     },
25419
25420     // private
25421     beginUpdate : function(){
25422         this.updating = true;
25423     },
25424
25425     // private
25426     endUpdate : function(){
25427         this.updating = false;
25428         this.sync(true);
25429     },
25430
25431     // private
25432     hideUnders : function(negOffset){
25433         if(this.shadow){
25434             this.shadow.hide();
25435         }
25436         this.hideShim();
25437     },
25438
25439     // private
25440     constrainXY : function(){
25441         if(this.constrain){
25442             var vw = Roo.lib.Dom.getViewWidth(),
25443                 vh = Roo.lib.Dom.getViewHeight();
25444             var s = Roo.get(document).getScroll();
25445
25446             var xy = this.getXY();
25447             var x = xy[0], y = xy[1];   
25448             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25449             // only move it if it needs it
25450             var moved = false;
25451             // first validate right/bottom
25452             if((x + w) > vw+s.left){
25453                 x = vw - w - this.shadowOffset;
25454                 moved = true;
25455             }
25456             if((y + h) > vh+s.top){
25457                 y = vh - h - this.shadowOffset;
25458                 moved = true;
25459             }
25460             // then make sure top/left isn't negative
25461             if(x < s.left){
25462                 x = s.left;
25463                 moved = true;
25464             }
25465             if(y < s.top){
25466                 y = s.top;
25467                 moved = true;
25468             }
25469             if(moved){
25470                 if(this.avoidY){
25471                     var ay = this.avoidY;
25472                     if(y <= ay && (y+h) >= ay){
25473                         y = ay-h-5;   
25474                     }
25475                 }
25476                 xy = [x, y];
25477                 this.storeXY(xy);
25478                 supr.setXY.call(this, xy);
25479                 this.sync();
25480             }
25481         }
25482     },
25483
25484     isVisible : function(){
25485         return this.visible;    
25486     },
25487
25488     // private
25489     showAction : function(){
25490         this.visible = true; // track visibility to prevent getStyle calls
25491         if(this.useDisplay === true){
25492             this.setDisplayed("");
25493         }else if(this.lastXY){
25494             supr.setXY.call(this, this.lastXY);
25495         }else if(this.lastLT){
25496             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25497         }
25498     },
25499
25500     // private
25501     hideAction : function(){
25502         this.visible = false;
25503         if(this.useDisplay === true){
25504             this.setDisplayed(false);
25505         }else{
25506             this.setLeftTop(-10000,-10000);
25507         }
25508     },
25509
25510     // overridden Element method
25511     setVisible : function(v, a, d, c, e){
25512         if(v){
25513             this.showAction();
25514         }
25515         if(a && v){
25516             var cb = function(){
25517                 this.sync(true);
25518                 if(c){
25519                     c();
25520                 }
25521             }.createDelegate(this);
25522             supr.setVisible.call(this, true, true, d, cb, e);
25523         }else{
25524             if(!v){
25525                 this.hideUnders(true);
25526             }
25527             var cb = c;
25528             if(a){
25529                 cb = function(){
25530                     this.hideAction();
25531                     if(c){
25532                         c();
25533                     }
25534                 }.createDelegate(this);
25535             }
25536             supr.setVisible.call(this, v, a, d, cb, e);
25537             if(v){
25538                 this.sync(true);
25539             }else if(!a){
25540                 this.hideAction();
25541             }
25542         }
25543     },
25544
25545     storeXY : function(xy){
25546         delete this.lastLT;
25547         this.lastXY = xy;
25548     },
25549
25550     storeLeftTop : function(left, top){
25551         delete this.lastXY;
25552         this.lastLT = [left, top];
25553     },
25554
25555     // private
25556     beforeFx : function(){
25557         this.beforeAction();
25558         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25559     },
25560
25561     // private
25562     afterFx : function(){
25563         Roo.Layer.superclass.afterFx.apply(this, arguments);
25564         this.sync(this.isVisible());
25565     },
25566
25567     // private
25568     beforeAction : function(){
25569         if(!this.updating && this.shadow){
25570             this.shadow.hide();
25571         }
25572     },
25573
25574     // overridden Element method
25575     setLeft : function(left){
25576         this.storeLeftTop(left, this.getTop(true));
25577         supr.setLeft.apply(this, arguments);
25578         this.sync();
25579     },
25580
25581     setTop : function(top){
25582         this.storeLeftTop(this.getLeft(true), top);
25583         supr.setTop.apply(this, arguments);
25584         this.sync();
25585     },
25586
25587     setLeftTop : function(left, top){
25588         this.storeLeftTop(left, top);
25589         supr.setLeftTop.apply(this, arguments);
25590         this.sync();
25591     },
25592
25593     setXY : function(xy, a, d, c, e){
25594         this.fixDisplay();
25595         this.beforeAction();
25596         this.storeXY(xy);
25597         var cb = this.createCB(c);
25598         supr.setXY.call(this, xy, a, d, cb, e);
25599         if(!a){
25600             cb();
25601         }
25602     },
25603
25604     // private
25605     createCB : function(c){
25606         var el = this;
25607         return function(){
25608             el.constrainXY();
25609             el.sync(true);
25610             if(c){
25611                 c();
25612             }
25613         };
25614     },
25615
25616     // overridden Element method
25617     setX : function(x, a, d, c, e){
25618         this.setXY([x, this.getY()], a, d, c, e);
25619     },
25620
25621     // overridden Element method
25622     setY : function(y, a, d, c, e){
25623         this.setXY([this.getX(), y], a, d, c, e);
25624     },
25625
25626     // overridden Element method
25627     setSize : function(w, h, a, d, c, e){
25628         this.beforeAction();
25629         var cb = this.createCB(c);
25630         supr.setSize.call(this, w, h, a, d, cb, e);
25631         if(!a){
25632             cb();
25633         }
25634     },
25635
25636     // overridden Element method
25637     setWidth : function(w, a, d, c, e){
25638         this.beforeAction();
25639         var cb = this.createCB(c);
25640         supr.setWidth.call(this, w, a, d, cb, e);
25641         if(!a){
25642             cb();
25643         }
25644     },
25645
25646     // overridden Element method
25647     setHeight : function(h, a, d, c, e){
25648         this.beforeAction();
25649         var cb = this.createCB(c);
25650         supr.setHeight.call(this, h, a, d, cb, e);
25651         if(!a){
25652             cb();
25653         }
25654     },
25655
25656     // overridden Element method
25657     setBounds : function(x, y, w, h, a, d, c, e){
25658         this.beforeAction();
25659         var cb = this.createCB(c);
25660         if(!a){
25661             this.storeXY([x, y]);
25662             supr.setXY.call(this, [x, y]);
25663             supr.setSize.call(this, w, h, a, d, cb, e);
25664             cb();
25665         }else{
25666             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25667         }
25668         return this;
25669     },
25670     
25671     /**
25672      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25673      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25674      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25675      * @param {Number} zindex The new z-index to set
25676      * @return {this} The Layer
25677      */
25678     setZIndex : function(zindex){
25679         this.zindex = zindex;
25680         this.setStyle("z-index", zindex + 2);
25681         if(this.shadow){
25682             this.shadow.setZIndex(zindex + 1);
25683         }
25684         if(this.shim){
25685             this.shim.setStyle("z-index", zindex);
25686         }
25687     }
25688 });
25689 })();/*
25690  * Based on:
25691  * Ext JS Library 1.1.1
25692  * Copyright(c) 2006-2007, Ext JS, LLC.
25693  *
25694  * Originally Released Under LGPL - original licence link has changed is not relivant.
25695  *
25696  * Fork - LGPL
25697  * <script type="text/javascript">
25698  */
25699
25700
25701 /**
25702  * @class Roo.Shadow
25703  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25704  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25705  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25706  * @constructor
25707  * Create a new Shadow
25708  * @param {Object} config The config object
25709  */
25710 Roo.Shadow = function(config){
25711     Roo.apply(this, config);
25712     if(typeof this.mode != "string"){
25713         this.mode = this.defaultMode;
25714     }
25715     var o = this.offset, a = {h: 0};
25716     var rad = Math.floor(this.offset/2);
25717     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25718         case "drop":
25719             a.w = 0;
25720             a.l = a.t = o;
25721             a.t -= 1;
25722             if(Roo.isIE){
25723                 a.l -= this.offset + rad;
25724                 a.t -= this.offset + rad;
25725                 a.w -= rad;
25726                 a.h -= rad;
25727                 a.t += 1;
25728             }
25729         break;
25730         case "sides":
25731             a.w = (o*2);
25732             a.l = -o;
25733             a.t = o-1;
25734             if(Roo.isIE){
25735                 a.l -= (this.offset - rad);
25736                 a.t -= this.offset + rad;
25737                 a.l += 1;
25738                 a.w -= (this.offset - rad)*2;
25739                 a.w -= rad + 1;
25740                 a.h -= 1;
25741             }
25742         break;
25743         case "frame":
25744             a.w = a.h = (o*2);
25745             a.l = a.t = -o;
25746             a.t += 1;
25747             a.h -= 2;
25748             if(Roo.isIE){
25749                 a.l -= (this.offset - rad);
25750                 a.t -= (this.offset - rad);
25751                 a.l += 1;
25752                 a.w -= (this.offset + rad + 1);
25753                 a.h -= (this.offset + rad);
25754                 a.h += 1;
25755             }
25756         break;
25757     };
25758
25759     this.adjusts = a;
25760 };
25761
25762 Roo.Shadow.prototype = {
25763     /**
25764      * @cfg {String} mode
25765      * The shadow display mode.  Supports the following options:<br />
25766      * sides: Shadow displays on both sides and bottom only<br />
25767      * frame: Shadow displays equally on all four sides<br />
25768      * drop: Traditional bottom-right drop shadow (default)
25769      */
25770     /**
25771      * @cfg {String} offset
25772      * The number of pixels to offset the shadow from the element (defaults to 4)
25773      */
25774     offset: 4,
25775
25776     // private
25777     defaultMode: "drop",
25778
25779     /**
25780      * Displays the shadow under the target element
25781      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25782      */
25783     show : function(target){
25784         target = Roo.get(target);
25785         if(!this.el){
25786             this.el = Roo.Shadow.Pool.pull();
25787             if(this.el.dom.nextSibling != target.dom){
25788                 this.el.insertBefore(target);
25789             }
25790         }
25791         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25792         if(Roo.isIE){
25793             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25794         }
25795         this.realign(
25796             target.getLeft(true),
25797             target.getTop(true),
25798             target.getWidth(),
25799             target.getHeight()
25800         );
25801         this.el.dom.style.display = "block";
25802     },
25803
25804     /**
25805      * Returns true if the shadow is visible, else false
25806      */
25807     isVisible : function(){
25808         return this.el ? true : false;  
25809     },
25810
25811     /**
25812      * Direct alignment when values are already available. Show must be called at least once before
25813      * calling this method to ensure it is initialized.
25814      * @param {Number} left The target element left position
25815      * @param {Number} top The target element top position
25816      * @param {Number} width The target element width
25817      * @param {Number} height The target element height
25818      */
25819     realign : function(l, t, w, h){
25820         if(!this.el){
25821             return;
25822         }
25823         var a = this.adjusts, d = this.el.dom, s = d.style;
25824         var iea = 0;
25825         s.left = (l+a.l)+"px";
25826         s.top = (t+a.t)+"px";
25827         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25828  
25829         if(s.width != sws || s.height != shs){
25830             s.width = sws;
25831             s.height = shs;
25832             if(!Roo.isIE){
25833                 var cn = d.childNodes;
25834                 var sww = Math.max(0, (sw-12))+"px";
25835                 cn[0].childNodes[1].style.width = sww;
25836                 cn[1].childNodes[1].style.width = sww;
25837                 cn[2].childNodes[1].style.width = sww;
25838                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25839             }
25840         }
25841     },
25842
25843     /**
25844      * Hides this shadow
25845      */
25846     hide : function(){
25847         if(this.el){
25848             this.el.dom.style.display = "none";
25849             Roo.Shadow.Pool.push(this.el);
25850             delete this.el;
25851         }
25852     },
25853
25854     /**
25855      * Adjust the z-index of this shadow
25856      * @param {Number} zindex The new z-index
25857      */
25858     setZIndex : function(z){
25859         this.zIndex = z;
25860         if(this.el){
25861             this.el.setStyle("z-index", z);
25862         }
25863     }
25864 };
25865
25866 // Private utility class that manages the internal Shadow cache
25867 Roo.Shadow.Pool = function(){
25868     var p = [];
25869     var markup = Roo.isIE ?
25870                  '<div class="x-ie-shadow"></div>' :
25871                  '<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>';
25872     return {
25873         pull : function(){
25874             var sh = p.shift();
25875             if(!sh){
25876                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25877                 sh.autoBoxAdjust = false;
25878             }
25879             return sh;
25880         },
25881
25882         push : function(sh){
25883             p.push(sh);
25884         }
25885     };
25886 }();/*
25887  * Based on:
25888  * Ext JS Library 1.1.1
25889  * Copyright(c) 2006-2007, Ext JS, LLC.
25890  *
25891  * Originally Released Under LGPL - original licence link has changed is not relivant.
25892  *
25893  * Fork - LGPL
25894  * <script type="text/javascript">
25895  */
25896
25897
25898 /**
25899  * @class Roo.SplitBar
25900  * @extends Roo.util.Observable
25901  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25902  * <br><br>
25903  * Usage:
25904  * <pre><code>
25905 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25906                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25907 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25908 split.minSize = 100;
25909 split.maxSize = 600;
25910 split.animate = true;
25911 split.on('moved', splitterMoved);
25912 </code></pre>
25913  * @constructor
25914  * Create a new SplitBar
25915  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25916  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25917  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25918  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25919                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25920                         position of the SplitBar).
25921  */
25922 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25923     
25924     /** @private */
25925     this.el = Roo.get(dragElement, true);
25926     this.el.dom.unselectable = "on";
25927     /** @private */
25928     this.resizingEl = Roo.get(resizingElement, true);
25929
25930     /**
25931      * @private
25932      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25933      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25934      * @type Number
25935      */
25936     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25937     
25938     /**
25939      * The minimum size of the resizing element. (Defaults to 0)
25940      * @type Number
25941      */
25942     this.minSize = 0;
25943     
25944     /**
25945      * The maximum size of the resizing element. (Defaults to 2000)
25946      * @type Number
25947      */
25948     this.maxSize = 2000;
25949     
25950     /**
25951      * Whether to animate the transition to the new size
25952      * @type Boolean
25953      */
25954     this.animate = false;
25955     
25956     /**
25957      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25958      * @type Boolean
25959      */
25960     this.useShim = false;
25961     
25962     /** @private */
25963     this.shim = null;
25964     
25965     if(!existingProxy){
25966         /** @private */
25967         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25968     }else{
25969         this.proxy = Roo.get(existingProxy).dom;
25970     }
25971     /** @private */
25972     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25973     
25974     /** @private */
25975     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25976     
25977     /** @private */
25978     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25979     
25980     /** @private */
25981     this.dragSpecs = {};
25982     
25983     /**
25984      * @private The adapter to use to positon and resize elements
25985      */
25986     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25987     this.adapter.init(this);
25988     
25989     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25990         /** @private */
25991         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25992         this.el.addClass("x-splitbar-h");
25993     }else{
25994         /** @private */
25995         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25996         this.el.addClass("x-splitbar-v");
25997     }
25998     
25999     this.addEvents({
26000         /**
26001          * @event resize
26002          * Fires when the splitter is moved (alias for {@link #event-moved})
26003          * @param {Roo.SplitBar} this
26004          * @param {Number} newSize the new width or height
26005          */
26006         "resize" : true,
26007         /**
26008          * @event moved
26009          * Fires when the splitter is moved
26010          * @param {Roo.SplitBar} this
26011          * @param {Number} newSize the new width or height
26012          */
26013         "moved" : true,
26014         /**
26015          * @event beforeresize
26016          * Fires before the splitter is dragged
26017          * @param {Roo.SplitBar} this
26018          */
26019         "beforeresize" : true,
26020
26021         "beforeapply" : true
26022     });
26023
26024     Roo.util.Observable.call(this);
26025 };
26026
26027 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26028     onStartProxyDrag : function(x, y){
26029         this.fireEvent("beforeresize", this);
26030         if(!this.overlay){
26031             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26032             o.unselectable();
26033             o.enableDisplayMode("block");
26034             // all splitbars share the same overlay
26035             Roo.SplitBar.prototype.overlay = o;
26036         }
26037         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26038         this.overlay.show();
26039         Roo.get(this.proxy).setDisplayed("block");
26040         var size = this.adapter.getElementSize(this);
26041         this.activeMinSize = this.getMinimumSize();;
26042         this.activeMaxSize = this.getMaximumSize();;
26043         var c1 = size - this.activeMinSize;
26044         var c2 = Math.max(this.activeMaxSize - size, 0);
26045         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26046             this.dd.resetConstraints();
26047             this.dd.setXConstraint(
26048                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26049                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26050             );
26051             this.dd.setYConstraint(0, 0);
26052         }else{
26053             this.dd.resetConstraints();
26054             this.dd.setXConstraint(0, 0);
26055             this.dd.setYConstraint(
26056                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26057                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26058             );
26059          }
26060         this.dragSpecs.startSize = size;
26061         this.dragSpecs.startPoint = [x, y];
26062         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26063     },
26064     
26065     /** 
26066      * @private Called after the drag operation by the DDProxy
26067      */
26068     onEndProxyDrag : function(e){
26069         Roo.get(this.proxy).setDisplayed(false);
26070         var endPoint = Roo.lib.Event.getXY(e);
26071         if(this.overlay){
26072             this.overlay.hide();
26073         }
26074         var newSize;
26075         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26076             newSize = this.dragSpecs.startSize + 
26077                 (this.placement == Roo.SplitBar.LEFT ?
26078                     endPoint[0] - this.dragSpecs.startPoint[0] :
26079                     this.dragSpecs.startPoint[0] - endPoint[0]
26080                 );
26081         }else{
26082             newSize = this.dragSpecs.startSize + 
26083                 (this.placement == Roo.SplitBar.TOP ?
26084                     endPoint[1] - this.dragSpecs.startPoint[1] :
26085                     this.dragSpecs.startPoint[1] - endPoint[1]
26086                 );
26087         }
26088         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26089         if(newSize != this.dragSpecs.startSize){
26090             if(this.fireEvent('beforeapply', this, newSize) !== false){
26091                 this.adapter.setElementSize(this, newSize);
26092                 this.fireEvent("moved", this, newSize);
26093                 this.fireEvent("resize", this, newSize);
26094             }
26095         }
26096     },
26097     
26098     /**
26099      * Get the adapter this SplitBar uses
26100      * @return The adapter object
26101      */
26102     getAdapter : function(){
26103         return this.adapter;
26104     },
26105     
26106     /**
26107      * Set the adapter this SplitBar uses
26108      * @param {Object} adapter A SplitBar adapter object
26109      */
26110     setAdapter : function(adapter){
26111         this.adapter = adapter;
26112         this.adapter.init(this);
26113     },
26114     
26115     /**
26116      * Gets the minimum size for the resizing element
26117      * @return {Number} The minimum size
26118      */
26119     getMinimumSize : function(){
26120         return this.minSize;
26121     },
26122     
26123     /**
26124      * Sets the minimum size for the resizing element
26125      * @param {Number} minSize The minimum size
26126      */
26127     setMinimumSize : function(minSize){
26128         this.minSize = minSize;
26129     },
26130     
26131     /**
26132      * Gets the maximum size for the resizing element
26133      * @return {Number} The maximum size
26134      */
26135     getMaximumSize : function(){
26136         return this.maxSize;
26137     },
26138     
26139     /**
26140      * Sets the maximum size for the resizing element
26141      * @param {Number} maxSize The maximum size
26142      */
26143     setMaximumSize : function(maxSize){
26144         this.maxSize = maxSize;
26145     },
26146     
26147     /**
26148      * Sets the initialize size for the resizing element
26149      * @param {Number} size The initial size
26150      */
26151     setCurrentSize : function(size){
26152         var oldAnimate = this.animate;
26153         this.animate = false;
26154         this.adapter.setElementSize(this, size);
26155         this.animate = oldAnimate;
26156     },
26157     
26158     /**
26159      * Destroy this splitbar. 
26160      * @param {Boolean} removeEl True to remove the element
26161      */
26162     destroy : function(removeEl){
26163         if(this.shim){
26164             this.shim.remove();
26165         }
26166         this.dd.unreg();
26167         this.proxy.parentNode.removeChild(this.proxy);
26168         if(removeEl){
26169             this.el.remove();
26170         }
26171     }
26172 });
26173
26174 /**
26175  * @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.
26176  */
26177 Roo.SplitBar.createProxy = function(dir){
26178     var proxy = new Roo.Element(document.createElement("div"));
26179     proxy.unselectable();
26180     var cls = 'x-splitbar-proxy';
26181     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26182     document.body.appendChild(proxy.dom);
26183     return proxy.dom;
26184 };
26185
26186 /** 
26187  * @class Roo.SplitBar.BasicLayoutAdapter
26188  * Default Adapter. It assumes the splitter and resizing element are not positioned
26189  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26190  */
26191 Roo.SplitBar.BasicLayoutAdapter = function(){
26192 };
26193
26194 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26195     // do nothing for now
26196     init : function(s){
26197     
26198     },
26199     /**
26200      * Called before drag operations to get the current size of the resizing element. 
26201      * @param {Roo.SplitBar} s The SplitBar using this adapter
26202      */
26203      getElementSize : function(s){
26204         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26205             return s.resizingEl.getWidth();
26206         }else{
26207             return s.resizingEl.getHeight();
26208         }
26209     },
26210     
26211     /**
26212      * Called after drag operations to set the size of the resizing element.
26213      * @param {Roo.SplitBar} s The SplitBar using this adapter
26214      * @param {Number} newSize The new size to set
26215      * @param {Function} onComplete A function to be invoked when resizing is complete
26216      */
26217     setElementSize : function(s, newSize, onComplete){
26218         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26219             if(!s.animate){
26220                 s.resizingEl.setWidth(newSize);
26221                 if(onComplete){
26222                     onComplete(s, newSize);
26223                 }
26224             }else{
26225                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26226             }
26227         }else{
26228             
26229             if(!s.animate){
26230                 s.resizingEl.setHeight(newSize);
26231                 if(onComplete){
26232                     onComplete(s, newSize);
26233                 }
26234             }else{
26235                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26236             }
26237         }
26238     }
26239 };
26240
26241 /** 
26242  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26243  * @extends Roo.SplitBar.BasicLayoutAdapter
26244  * Adapter that  moves the splitter element to align with the resized sizing element. 
26245  * Used with an absolute positioned SplitBar.
26246  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26247  * document.body, make sure you assign an id to the body element.
26248  */
26249 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26250     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26251     this.container = Roo.get(container);
26252 };
26253
26254 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26255     init : function(s){
26256         this.basic.init(s);
26257     },
26258     
26259     getElementSize : function(s){
26260         return this.basic.getElementSize(s);
26261     },
26262     
26263     setElementSize : function(s, newSize, onComplete){
26264         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26265     },
26266     
26267     moveSplitter : function(s){
26268         var yes = Roo.SplitBar;
26269         switch(s.placement){
26270             case yes.LEFT:
26271                 s.el.setX(s.resizingEl.getRight());
26272                 break;
26273             case yes.RIGHT:
26274                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26275                 break;
26276             case yes.TOP:
26277                 s.el.setY(s.resizingEl.getBottom());
26278                 break;
26279             case yes.BOTTOM:
26280                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26281                 break;
26282         }
26283     }
26284 };
26285
26286 /**
26287  * Orientation constant - Create a vertical SplitBar
26288  * @static
26289  * @type Number
26290  */
26291 Roo.SplitBar.VERTICAL = 1;
26292
26293 /**
26294  * Orientation constant - Create a horizontal SplitBar
26295  * @static
26296  * @type Number
26297  */
26298 Roo.SplitBar.HORIZONTAL = 2;
26299
26300 /**
26301  * Placement constant - The resizing element is to the left of the splitter element
26302  * @static
26303  * @type Number
26304  */
26305 Roo.SplitBar.LEFT = 1;
26306
26307 /**
26308  * Placement constant - The resizing element is to the right of the splitter element
26309  * @static
26310  * @type Number
26311  */
26312 Roo.SplitBar.RIGHT = 2;
26313
26314 /**
26315  * Placement constant - The resizing element is positioned above the splitter element
26316  * @static
26317  * @type Number
26318  */
26319 Roo.SplitBar.TOP = 3;
26320
26321 /**
26322  * Placement constant - The resizing element is positioned under splitter element
26323  * @static
26324  * @type Number
26325  */
26326 Roo.SplitBar.BOTTOM = 4;
26327 /*
26328  * Based on:
26329  * Ext JS Library 1.1.1
26330  * Copyright(c) 2006-2007, Ext JS, LLC.
26331  *
26332  * Originally Released Under LGPL - original licence link has changed is not relivant.
26333  *
26334  * Fork - LGPL
26335  * <script type="text/javascript">
26336  */
26337
26338 /**
26339  * @class Roo.View
26340  * @extends Roo.util.Observable
26341  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26342  * This class also supports single and multi selection modes. <br>
26343  * Create a data model bound view:
26344  <pre><code>
26345  var store = new Roo.data.Store(...);
26346
26347  var view = new Roo.View({
26348     el : "my-element",
26349     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26350  
26351     singleSelect: true,
26352     selectedClass: "ydataview-selected",
26353     store: store
26354  });
26355
26356  // listen for node click?
26357  view.on("click", function(vw, index, node, e){
26358  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26359  });
26360
26361  // load XML data
26362  dataModel.load("foobar.xml");
26363  </code></pre>
26364  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26365  * <br><br>
26366  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26367  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26368  * 
26369  * Note: old style constructor is still suported (container, template, config)
26370  * 
26371  * @constructor
26372  * Create a new View
26373  * @param {Object} config The config object
26374  * 
26375  */
26376 Roo.View = function(config, depreciated_tpl, depreciated_config){
26377     
26378     this.parent = false;
26379     
26380     if (typeof(depreciated_tpl) == 'undefined') {
26381         // new way.. - universal constructor.
26382         Roo.apply(this, config);
26383         this.el  = Roo.get(this.el);
26384     } else {
26385         // old format..
26386         this.el  = Roo.get(config);
26387         this.tpl = depreciated_tpl;
26388         Roo.apply(this, depreciated_config);
26389     }
26390     this.wrapEl  = this.el.wrap().wrap();
26391     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26392     
26393     
26394     if(typeof(this.tpl) == "string"){
26395         this.tpl = new Roo.Template(this.tpl);
26396     } else {
26397         // support xtype ctors..
26398         this.tpl = new Roo.factory(this.tpl, Roo);
26399     }
26400     
26401     
26402     this.tpl.compile();
26403     
26404     /** @private */
26405     this.addEvents({
26406         /**
26407          * @event beforeclick
26408          * Fires before a click is processed. Returns false to cancel the default action.
26409          * @param {Roo.View} this
26410          * @param {Number} index The index of the target node
26411          * @param {HTMLElement} node The target node
26412          * @param {Roo.EventObject} e The raw event object
26413          */
26414             "beforeclick" : true,
26415         /**
26416          * @event click
26417          * Fires when a template node is clicked.
26418          * @param {Roo.View} this
26419          * @param {Number} index The index of the target node
26420          * @param {HTMLElement} node The target node
26421          * @param {Roo.EventObject} e The raw event object
26422          */
26423             "click" : true,
26424         /**
26425          * @event dblclick
26426          * Fires when a template node is double clicked.
26427          * @param {Roo.View} this
26428          * @param {Number} index The index of the target node
26429          * @param {HTMLElement} node The target node
26430          * @param {Roo.EventObject} e The raw event object
26431          */
26432             "dblclick" : true,
26433         /**
26434          * @event contextmenu
26435          * Fires when a template node is right clicked.
26436          * @param {Roo.View} this
26437          * @param {Number} index The index of the target node
26438          * @param {HTMLElement} node The target node
26439          * @param {Roo.EventObject} e The raw event object
26440          */
26441             "contextmenu" : true,
26442         /**
26443          * @event selectionchange
26444          * Fires when the selected nodes change.
26445          * @param {Roo.View} this
26446          * @param {Array} selections Array of the selected nodes
26447          */
26448             "selectionchange" : true,
26449     
26450         /**
26451          * @event beforeselect
26452          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26453          * @param {Roo.View} this
26454          * @param {HTMLElement} node The node to be selected
26455          * @param {Array} selections Array of currently selected nodes
26456          */
26457             "beforeselect" : true,
26458         /**
26459          * @event preparedata
26460          * Fires on every row to render, to allow you to change the data.
26461          * @param {Roo.View} this
26462          * @param {Object} data to be rendered (change this)
26463          */
26464           "preparedata" : true
26465           
26466           
26467         });
26468
26469
26470
26471     this.el.on({
26472         "click": this.onClick,
26473         "dblclick": this.onDblClick,
26474         "contextmenu": this.onContextMenu,
26475         scope:this
26476     });
26477
26478     this.selections = [];
26479     this.nodes = [];
26480     this.cmp = new Roo.CompositeElementLite([]);
26481     if(this.store){
26482         this.store = Roo.factory(this.store, Roo.data);
26483         this.setStore(this.store, true);
26484     }
26485     
26486     if ( this.footer && this.footer.xtype) {
26487            
26488          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26489         
26490         this.footer.dataSource = this.store;
26491         this.footer.container = fctr;
26492         this.footer = Roo.factory(this.footer, Roo);
26493         fctr.insertFirst(this.el);
26494         
26495         // this is a bit insane - as the paging toolbar seems to detach the el..
26496 //        dom.parentNode.parentNode.parentNode
26497          // they get detached?
26498     }
26499     
26500     
26501     Roo.View.superclass.constructor.call(this);
26502     
26503     
26504 };
26505
26506 Roo.extend(Roo.View, Roo.util.Observable, {
26507     
26508      /**
26509      * @cfg {Roo.data.Store} store Data store to load data from.
26510      */
26511     store : false,
26512     
26513     /**
26514      * @cfg {String|Roo.Element} el The container element.
26515      */
26516     el : '',
26517     
26518     /**
26519      * @cfg {String|Roo.Template} tpl The template used by this View 
26520      */
26521     tpl : false,
26522     /**
26523      * @cfg {String} dataName the named area of the template to use as the data area
26524      *                          Works with domtemplates roo-name="name"
26525      */
26526     dataName: false,
26527     /**
26528      * @cfg {String} selectedClass The css class to add to selected nodes
26529      */
26530     selectedClass : "x-view-selected",
26531      /**
26532      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26533      */
26534     emptyText : "",
26535     
26536     /**
26537      * @cfg {String} text to display on mask (default Loading)
26538      */
26539     mask : false,
26540     /**
26541      * @cfg {Boolean} multiSelect Allow multiple selection
26542      */
26543     multiSelect : false,
26544     /**
26545      * @cfg {Boolean} singleSelect Allow single selection
26546      */
26547     singleSelect:  false,
26548     
26549     /**
26550      * @cfg {Boolean} toggleSelect - selecting 
26551      */
26552     toggleSelect : false,
26553     
26554     /**
26555      * @cfg {Boolean} tickable - selecting 
26556      */
26557     tickable : false,
26558     
26559     /**
26560      * Returns the element this view is bound to.
26561      * @return {Roo.Element}
26562      */
26563     getEl : function(){
26564         return this.wrapEl;
26565     },
26566     
26567     
26568
26569     /**
26570      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26571      */
26572     refresh : function(){
26573         //Roo.log('refresh');
26574         var t = this.tpl;
26575         
26576         // if we are using something like 'domtemplate', then
26577         // the what gets used is:
26578         // t.applySubtemplate(NAME, data, wrapping data..)
26579         // the outer template then get' applied with
26580         //     the store 'extra data'
26581         // and the body get's added to the
26582         //      roo-name="data" node?
26583         //      <span class='roo-tpl-{name}'></span> ?????
26584         
26585         
26586         
26587         this.clearSelections();
26588         this.el.update("");
26589         var html = [];
26590         var records = this.store.getRange();
26591         if(records.length < 1) {
26592             
26593             // is this valid??  = should it render a template??
26594             
26595             this.el.update(this.emptyText);
26596             return;
26597         }
26598         var el = this.el;
26599         if (this.dataName) {
26600             this.el.update(t.apply(this.store.meta)); //????
26601             el = this.el.child('.roo-tpl-' + this.dataName);
26602         }
26603         
26604         for(var i = 0, len = records.length; i < len; i++){
26605             var data = this.prepareData(records[i].data, i, records[i]);
26606             this.fireEvent("preparedata", this, data, i, records[i]);
26607             
26608             var d = Roo.apply({}, data);
26609             
26610             if(this.tickable){
26611                 Roo.apply(d, {'roo-id' : Roo.id()});
26612                 
26613                 var _this = this;
26614             
26615                 Roo.each(this.parent.item, function(item){
26616                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26617                         return;
26618                     }
26619                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26620                 });
26621             }
26622             
26623             html[html.length] = Roo.util.Format.trim(
26624                 this.dataName ?
26625                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26626                     t.apply(d)
26627             );
26628         }
26629         
26630         
26631         
26632         el.update(html.join(""));
26633         this.nodes = el.dom.childNodes;
26634         this.updateIndexes(0);
26635     },
26636     
26637
26638     /**
26639      * Function to override to reformat the data that is sent to
26640      * the template for each node.
26641      * DEPRICATED - use the preparedata event handler.
26642      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26643      * a JSON object for an UpdateManager bound view).
26644      */
26645     prepareData : function(data, index, record)
26646     {
26647         this.fireEvent("preparedata", this, data, index, record);
26648         return data;
26649     },
26650
26651     onUpdate : function(ds, record){
26652         // Roo.log('on update');   
26653         this.clearSelections();
26654         var index = this.store.indexOf(record);
26655         var n = this.nodes[index];
26656         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26657         n.parentNode.removeChild(n);
26658         this.updateIndexes(index, index);
26659     },
26660
26661     
26662     
26663 // --------- FIXME     
26664     onAdd : function(ds, records, index)
26665     {
26666         //Roo.log(['on Add', ds, records, index] );        
26667         this.clearSelections();
26668         if(this.nodes.length == 0){
26669             this.refresh();
26670             return;
26671         }
26672         var n = this.nodes[index];
26673         for(var i = 0, len = records.length; i < len; i++){
26674             var d = this.prepareData(records[i].data, i, records[i]);
26675             if(n){
26676                 this.tpl.insertBefore(n, d);
26677             }else{
26678                 
26679                 this.tpl.append(this.el, d);
26680             }
26681         }
26682         this.updateIndexes(index);
26683     },
26684
26685     onRemove : function(ds, record, index){
26686        // Roo.log('onRemove');
26687         this.clearSelections();
26688         var el = this.dataName  ?
26689             this.el.child('.roo-tpl-' + this.dataName) :
26690             this.el; 
26691         
26692         el.dom.removeChild(this.nodes[index]);
26693         this.updateIndexes(index);
26694     },
26695
26696     /**
26697      * Refresh an individual node.
26698      * @param {Number} index
26699      */
26700     refreshNode : function(index){
26701         this.onUpdate(this.store, this.store.getAt(index));
26702     },
26703
26704     updateIndexes : function(startIndex, endIndex){
26705         var ns = this.nodes;
26706         startIndex = startIndex || 0;
26707         endIndex = endIndex || ns.length - 1;
26708         for(var i = startIndex; i <= endIndex; i++){
26709             ns[i].nodeIndex = i;
26710         }
26711     },
26712
26713     /**
26714      * Changes the data store this view uses and refresh the view.
26715      * @param {Store} store
26716      */
26717     setStore : function(store, initial){
26718         if(!initial && this.store){
26719             this.store.un("datachanged", this.refresh);
26720             this.store.un("add", this.onAdd);
26721             this.store.un("remove", this.onRemove);
26722             this.store.un("update", this.onUpdate);
26723             this.store.un("clear", this.refresh);
26724             this.store.un("beforeload", this.onBeforeLoad);
26725             this.store.un("load", this.onLoad);
26726             this.store.un("loadexception", this.onLoad);
26727         }
26728         if(store){
26729           
26730             store.on("datachanged", this.refresh, this);
26731             store.on("add", this.onAdd, this);
26732             store.on("remove", this.onRemove, this);
26733             store.on("update", this.onUpdate, this);
26734             store.on("clear", this.refresh, this);
26735             store.on("beforeload", this.onBeforeLoad, this);
26736             store.on("load", this.onLoad, this);
26737             store.on("loadexception", this.onLoad, this);
26738         }
26739         
26740         if(store){
26741             this.refresh();
26742         }
26743     },
26744     /**
26745      * onbeforeLoad - masks the loading area.
26746      *
26747      */
26748     onBeforeLoad : function(store,opts)
26749     {
26750          //Roo.log('onBeforeLoad');   
26751         if (!opts.add) {
26752             this.el.update("");
26753         }
26754         this.el.mask(this.mask ? this.mask : "Loading" ); 
26755     },
26756     onLoad : function ()
26757     {
26758         this.el.unmask();
26759     },
26760     
26761
26762     /**
26763      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26764      * @param {HTMLElement} node
26765      * @return {HTMLElement} The template node
26766      */
26767     findItemFromChild : function(node){
26768         var el = this.dataName  ?
26769             this.el.child('.roo-tpl-' + this.dataName,true) :
26770             this.el.dom; 
26771         
26772         if(!node || node.parentNode == el){
26773                     return node;
26774             }
26775             var p = node.parentNode;
26776             while(p && p != el){
26777             if(p.parentNode == el){
26778                 return p;
26779             }
26780             p = p.parentNode;
26781         }
26782             return null;
26783     },
26784
26785     /** @ignore */
26786     onClick : function(e){
26787         var item = this.findItemFromChild(e.getTarget());
26788         if(item){
26789             var index = this.indexOf(item);
26790             if(this.onItemClick(item, index, e) !== false){
26791                 this.fireEvent("click", this, index, item, e);
26792             }
26793         }else{
26794             this.clearSelections();
26795         }
26796     },
26797
26798     /** @ignore */
26799     onContextMenu : function(e){
26800         var item = this.findItemFromChild(e.getTarget());
26801         if(item){
26802             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26803         }
26804     },
26805
26806     /** @ignore */
26807     onDblClick : function(e){
26808         var item = this.findItemFromChild(e.getTarget());
26809         if(item){
26810             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26811         }
26812     },
26813
26814     onItemClick : function(item, index, e)
26815     {
26816         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26817             return false;
26818         }
26819         if (this.toggleSelect) {
26820             var m = this.isSelected(item) ? 'unselect' : 'select';
26821             //Roo.log(m);
26822             var _t = this;
26823             _t[m](item, true, false);
26824             return true;
26825         }
26826         if(this.multiSelect || this.singleSelect){
26827             if(this.multiSelect && e.shiftKey && this.lastSelection){
26828                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26829             }else{
26830                 this.select(item, this.multiSelect && e.ctrlKey);
26831                 this.lastSelection = item;
26832             }
26833             
26834             if(!this.tickable){
26835                 e.preventDefault();
26836             }
26837             
26838         }
26839         return true;
26840     },
26841
26842     /**
26843      * Get the number of selected nodes.
26844      * @return {Number}
26845      */
26846     getSelectionCount : function(){
26847         return this.selections.length;
26848     },
26849
26850     /**
26851      * Get the currently selected nodes.
26852      * @return {Array} An array of HTMLElements
26853      */
26854     getSelectedNodes : function(){
26855         return this.selections;
26856     },
26857
26858     /**
26859      * Get the indexes of the selected nodes.
26860      * @return {Array}
26861      */
26862     getSelectedIndexes : function(){
26863         var indexes = [], s = this.selections;
26864         for(var i = 0, len = s.length; i < len; i++){
26865             indexes.push(s[i].nodeIndex);
26866         }
26867         return indexes;
26868     },
26869
26870     /**
26871      * Clear all selections
26872      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26873      */
26874     clearSelections : function(suppressEvent){
26875         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26876             this.cmp.elements = this.selections;
26877             this.cmp.removeClass(this.selectedClass);
26878             this.selections = [];
26879             if(!suppressEvent){
26880                 this.fireEvent("selectionchange", this, this.selections);
26881             }
26882         }
26883     },
26884
26885     /**
26886      * Returns true if the passed node is selected
26887      * @param {HTMLElement/Number} node The node or node index
26888      * @return {Boolean}
26889      */
26890     isSelected : function(node){
26891         var s = this.selections;
26892         if(s.length < 1){
26893             return false;
26894         }
26895         node = this.getNode(node);
26896         return s.indexOf(node) !== -1;
26897     },
26898
26899     /**
26900      * Selects nodes.
26901      * @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
26902      * @param {Boolean} keepExisting (optional) true to keep existing selections
26903      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26904      */
26905     select : function(nodeInfo, keepExisting, suppressEvent){
26906         if(nodeInfo instanceof Array){
26907             if(!keepExisting){
26908                 this.clearSelections(true);
26909             }
26910             for(var i = 0, len = nodeInfo.length; i < len; i++){
26911                 this.select(nodeInfo[i], true, true);
26912             }
26913             return;
26914         } 
26915         var node = this.getNode(nodeInfo);
26916         if(!node || this.isSelected(node)){
26917             return; // already selected.
26918         }
26919         if(!keepExisting){
26920             this.clearSelections(true);
26921         }
26922         
26923         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26924             Roo.fly(node).addClass(this.selectedClass);
26925             this.selections.push(node);
26926             if(!suppressEvent){
26927                 this.fireEvent("selectionchange", this, this.selections);
26928             }
26929         }
26930         
26931         
26932     },
26933       /**
26934      * Unselects nodes.
26935      * @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
26936      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26937      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26938      */
26939     unselect : function(nodeInfo, keepExisting, suppressEvent)
26940     {
26941         if(nodeInfo instanceof Array){
26942             Roo.each(this.selections, function(s) {
26943                 this.unselect(s, nodeInfo);
26944             }, this);
26945             return;
26946         }
26947         var node = this.getNode(nodeInfo);
26948         if(!node || !this.isSelected(node)){
26949             //Roo.log("not selected");
26950             return; // not selected.
26951         }
26952         // fireevent???
26953         var ns = [];
26954         Roo.each(this.selections, function(s) {
26955             if (s == node ) {
26956                 Roo.fly(node).removeClass(this.selectedClass);
26957
26958                 return;
26959             }
26960             ns.push(s);
26961         },this);
26962         
26963         this.selections= ns;
26964         this.fireEvent("selectionchange", this, this.selections);
26965     },
26966
26967     /**
26968      * Gets a template node.
26969      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26970      * @return {HTMLElement} The node or null if it wasn't found
26971      */
26972     getNode : function(nodeInfo){
26973         if(typeof nodeInfo == "string"){
26974             return document.getElementById(nodeInfo);
26975         }else if(typeof nodeInfo == "number"){
26976             return this.nodes[nodeInfo];
26977         }
26978         return nodeInfo;
26979     },
26980
26981     /**
26982      * Gets a range template nodes.
26983      * @param {Number} startIndex
26984      * @param {Number} endIndex
26985      * @return {Array} An array of nodes
26986      */
26987     getNodes : function(start, end){
26988         var ns = this.nodes;
26989         start = start || 0;
26990         end = typeof end == "undefined" ? ns.length - 1 : end;
26991         var nodes = [];
26992         if(start <= end){
26993             for(var i = start; i <= end; i++){
26994                 nodes.push(ns[i]);
26995             }
26996         } else{
26997             for(var i = start; i >= end; i--){
26998                 nodes.push(ns[i]);
26999             }
27000         }
27001         return nodes;
27002     },
27003
27004     /**
27005      * Finds the index of the passed node
27006      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27007      * @return {Number} The index of the node or -1
27008      */
27009     indexOf : function(node){
27010         node = this.getNode(node);
27011         if(typeof node.nodeIndex == "number"){
27012             return node.nodeIndex;
27013         }
27014         var ns = this.nodes;
27015         for(var i = 0, len = ns.length; i < len; i++){
27016             if(ns[i] == node){
27017                 return i;
27018             }
27019         }
27020         return -1;
27021     }
27022 });
27023 /*
27024  * Based on:
27025  * Ext JS Library 1.1.1
27026  * Copyright(c) 2006-2007, Ext JS, LLC.
27027  *
27028  * Originally Released Under LGPL - original licence link has changed is not relivant.
27029  *
27030  * Fork - LGPL
27031  * <script type="text/javascript">
27032  */
27033
27034 /**
27035  * @class Roo.JsonView
27036  * @extends Roo.View
27037  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27038 <pre><code>
27039 var view = new Roo.JsonView({
27040     container: "my-element",
27041     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27042     multiSelect: true, 
27043     jsonRoot: "data" 
27044 });
27045
27046 // listen for node click?
27047 view.on("click", function(vw, index, node, e){
27048     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27049 });
27050
27051 // direct load of JSON data
27052 view.load("foobar.php");
27053
27054 // Example from my blog list
27055 var tpl = new Roo.Template(
27056     '&lt;div class="entry"&gt;' +
27057     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27058     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27059     "&lt;/div&gt;&lt;hr /&gt;"
27060 );
27061
27062 var moreView = new Roo.JsonView({
27063     container :  "entry-list", 
27064     template : tpl,
27065     jsonRoot: "posts"
27066 });
27067 moreView.on("beforerender", this.sortEntries, this);
27068 moreView.load({
27069     url: "/blog/get-posts.php",
27070     params: "allposts=true",
27071     text: "Loading Blog Entries..."
27072 });
27073 </code></pre>
27074
27075 * Note: old code is supported with arguments : (container, template, config)
27076
27077
27078  * @constructor
27079  * Create a new JsonView
27080  * 
27081  * @param {Object} config The config object
27082  * 
27083  */
27084 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27085     
27086     
27087     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27088
27089     var um = this.el.getUpdateManager();
27090     um.setRenderer(this);
27091     um.on("update", this.onLoad, this);
27092     um.on("failure", this.onLoadException, this);
27093
27094     /**
27095      * @event beforerender
27096      * Fires before rendering of the downloaded JSON data.
27097      * @param {Roo.JsonView} this
27098      * @param {Object} data The JSON data loaded
27099      */
27100     /**
27101      * @event load
27102      * Fires when data is loaded.
27103      * @param {Roo.JsonView} this
27104      * @param {Object} data The JSON data loaded
27105      * @param {Object} response The raw Connect response object
27106      */
27107     /**
27108      * @event loadexception
27109      * Fires when loading fails.
27110      * @param {Roo.JsonView} this
27111      * @param {Object} response The raw Connect response object
27112      */
27113     this.addEvents({
27114         'beforerender' : true,
27115         'load' : true,
27116         'loadexception' : true
27117     });
27118 };
27119 Roo.extend(Roo.JsonView, Roo.View, {
27120     /**
27121      * @type {String} The root property in the loaded JSON object that contains the data
27122      */
27123     jsonRoot : "",
27124
27125     /**
27126      * Refreshes the view.
27127      */
27128     refresh : function(){
27129         this.clearSelections();
27130         this.el.update("");
27131         var html = [];
27132         var o = this.jsonData;
27133         if(o && o.length > 0){
27134             for(var i = 0, len = o.length; i < len; i++){
27135                 var data = this.prepareData(o[i], i, o);
27136                 html[html.length] = this.tpl.apply(data);
27137             }
27138         }else{
27139             html.push(this.emptyText);
27140         }
27141         this.el.update(html.join(""));
27142         this.nodes = this.el.dom.childNodes;
27143         this.updateIndexes(0);
27144     },
27145
27146     /**
27147      * 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.
27148      * @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:
27149      <pre><code>
27150      view.load({
27151          url: "your-url.php",
27152          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27153          callback: yourFunction,
27154          scope: yourObject, //(optional scope)
27155          discardUrl: false,
27156          nocache: false,
27157          text: "Loading...",
27158          timeout: 30,
27159          scripts: false
27160      });
27161      </code></pre>
27162      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27163      * 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.
27164      * @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}
27165      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27166      * @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.
27167      */
27168     load : function(){
27169         var um = this.el.getUpdateManager();
27170         um.update.apply(um, arguments);
27171     },
27172
27173     // note - render is a standard framework call...
27174     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27175     render : function(el, response){
27176         
27177         this.clearSelections();
27178         this.el.update("");
27179         var o;
27180         try{
27181             if (response != '') {
27182                 o = Roo.util.JSON.decode(response.responseText);
27183                 if(this.jsonRoot){
27184                     
27185                     o = o[this.jsonRoot];
27186                 }
27187             }
27188         } catch(e){
27189         }
27190         /**
27191          * The current JSON data or null
27192          */
27193         this.jsonData = o;
27194         this.beforeRender();
27195         this.refresh();
27196     },
27197
27198 /**
27199  * Get the number of records in the current JSON dataset
27200  * @return {Number}
27201  */
27202     getCount : function(){
27203         return this.jsonData ? this.jsonData.length : 0;
27204     },
27205
27206 /**
27207  * Returns the JSON object for the specified node(s)
27208  * @param {HTMLElement/Array} node The node or an array of nodes
27209  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27210  * you get the JSON object for the node
27211  */
27212     getNodeData : function(node){
27213         if(node instanceof Array){
27214             var data = [];
27215             for(var i = 0, len = node.length; i < len; i++){
27216                 data.push(this.getNodeData(node[i]));
27217             }
27218             return data;
27219         }
27220         return this.jsonData[this.indexOf(node)] || null;
27221     },
27222
27223     beforeRender : function(){
27224         this.snapshot = this.jsonData;
27225         if(this.sortInfo){
27226             this.sort.apply(this, this.sortInfo);
27227         }
27228         this.fireEvent("beforerender", this, this.jsonData);
27229     },
27230
27231     onLoad : function(el, o){
27232         this.fireEvent("load", this, this.jsonData, o);
27233     },
27234
27235     onLoadException : function(el, o){
27236         this.fireEvent("loadexception", this, o);
27237     },
27238
27239 /**
27240  * Filter the data by a specific property.
27241  * @param {String} property A property on your JSON objects
27242  * @param {String/RegExp} value Either string that the property values
27243  * should start with, or a RegExp to test against the property
27244  */
27245     filter : function(property, value){
27246         if(this.jsonData){
27247             var data = [];
27248             var ss = this.snapshot;
27249             if(typeof value == "string"){
27250                 var vlen = value.length;
27251                 if(vlen == 0){
27252                     this.clearFilter();
27253                     return;
27254                 }
27255                 value = value.toLowerCase();
27256                 for(var i = 0, len = ss.length; i < len; i++){
27257                     var o = ss[i];
27258                     if(o[property].substr(0, vlen).toLowerCase() == value){
27259                         data.push(o);
27260                     }
27261                 }
27262             } else if(value.exec){ // regex?
27263                 for(var i = 0, len = ss.length; i < len; i++){
27264                     var o = ss[i];
27265                     if(value.test(o[property])){
27266                         data.push(o);
27267                     }
27268                 }
27269             } else{
27270                 return;
27271             }
27272             this.jsonData = data;
27273             this.refresh();
27274         }
27275     },
27276
27277 /**
27278  * Filter by a function. The passed function will be called with each
27279  * object in the current dataset. If the function returns true the value is kept,
27280  * otherwise it is filtered.
27281  * @param {Function} fn
27282  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27283  */
27284     filterBy : function(fn, scope){
27285         if(this.jsonData){
27286             var data = [];
27287             var ss = this.snapshot;
27288             for(var i = 0, len = ss.length; i < len; i++){
27289                 var o = ss[i];
27290                 if(fn.call(scope || this, o)){
27291                     data.push(o);
27292                 }
27293             }
27294             this.jsonData = data;
27295             this.refresh();
27296         }
27297     },
27298
27299 /**
27300  * Clears the current filter.
27301  */
27302     clearFilter : function(){
27303         if(this.snapshot && this.jsonData != this.snapshot){
27304             this.jsonData = this.snapshot;
27305             this.refresh();
27306         }
27307     },
27308
27309
27310 /**
27311  * Sorts the data for this view and refreshes it.
27312  * @param {String} property A property on your JSON objects to sort on
27313  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27314  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27315  */
27316     sort : function(property, dir, sortType){
27317         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27318         if(this.jsonData){
27319             var p = property;
27320             var dsc = dir && dir.toLowerCase() == "desc";
27321             var f = function(o1, o2){
27322                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27323                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27324                 ;
27325                 if(v1 < v2){
27326                     return dsc ? +1 : -1;
27327                 } else if(v1 > v2){
27328                     return dsc ? -1 : +1;
27329                 } else{
27330                     return 0;
27331                 }
27332             };
27333             this.jsonData.sort(f);
27334             this.refresh();
27335             if(this.jsonData != this.snapshot){
27336                 this.snapshot.sort(f);
27337             }
27338         }
27339     }
27340 });/*
27341  * Based on:
27342  * Ext JS Library 1.1.1
27343  * Copyright(c) 2006-2007, Ext JS, LLC.
27344  *
27345  * Originally Released Under LGPL - original licence link has changed is not relivant.
27346  *
27347  * Fork - LGPL
27348  * <script type="text/javascript">
27349  */
27350  
27351
27352 /**
27353  * @class Roo.ColorPalette
27354  * @extends Roo.Component
27355  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27356  * Here's an example of typical usage:
27357  * <pre><code>
27358 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27359 cp.render('my-div');
27360
27361 cp.on('select', function(palette, selColor){
27362     // do something with selColor
27363 });
27364 </code></pre>
27365  * @constructor
27366  * Create a new ColorPalette
27367  * @param {Object} config The config object
27368  */
27369 Roo.ColorPalette = function(config){
27370     Roo.ColorPalette.superclass.constructor.call(this, config);
27371     this.addEvents({
27372         /**
27373              * @event select
27374              * Fires when a color is selected
27375              * @param {ColorPalette} this
27376              * @param {String} color The 6-digit color hex code (without the # symbol)
27377              */
27378         select: true
27379     });
27380
27381     if(this.handler){
27382         this.on("select", this.handler, this.scope, true);
27383     }
27384 };
27385 Roo.extend(Roo.ColorPalette, Roo.Component, {
27386     /**
27387      * @cfg {String} itemCls
27388      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27389      */
27390     itemCls : "x-color-palette",
27391     /**
27392      * @cfg {String} value
27393      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27394      * the hex codes are case-sensitive.
27395      */
27396     value : null,
27397     clickEvent:'click',
27398     // private
27399     ctype: "Roo.ColorPalette",
27400
27401     /**
27402      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27403      */
27404     allowReselect : false,
27405
27406     /**
27407      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27408      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27409      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27410      * of colors with the width setting until the box is symmetrical.</p>
27411      * <p>You can override individual colors if needed:</p>
27412      * <pre><code>
27413 var cp = new Roo.ColorPalette();
27414 cp.colors[0] = "FF0000";  // change the first box to red
27415 </code></pre>
27416
27417 Or you can provide a custom array of your own for complete control:
27418 <pre><code>
27419 var cp = new Roo.ColorPalette();
27420 cp.colors = ["000000", "993300", "333300"];
27421 </code></pre>
27422      * @type Array
27423      */
27424     colors : [
27425         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27426         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27427         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27428         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27429         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27430     ],
27431
27432     // private
27433     onRender : function(container, position){
27434         var t = new Roo.MasterTemplate(
27435             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27436         );
27437         var c = this.colors;
27438         for(var i = 0, len = c.length; i < len; i++){
27439             t.add([c[i]]);
27440         }
27441         var el = document.createElement("div");
27442         el.className = this.itemCls;
27443         t.overwrite(el);
27444         container.dom.insertBefore(el, position);
27445         this.el = Roo.get(el);
27446         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27447         if(this.clickEvent != 'click'){
27448             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27449         }
27450     },
27451
27452     // private
27453     afterRender : function(){
27454         Roo.ColorPalette.superclass.afterRender.call(this);
27455         if(this.value){
27456             var s = this.value;
27457             this.value = null;
27458             this.select(s);
27459         }
27460     },
27461
27462     // private
27463     handleClick : function(e, t){
27464         e.preventDefault();
27465         if(!this.disabled){
27466             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27467             this.select(c.toUpperCase());
27468         }
27469     },
27470
27471     /**
27472      * Selects the specified color in the palette (fires the select event)
27473      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27474      */
27475     select : function(color){
27476         color = color.replace("#", "");
27477         if(color != this.value || this.allowReselect){
27478             var el = this.el;
27479             if(this.value){
27480                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27481             }
27482             el.child("a.color-"+color).addClass("x-color-palette-sel");
27483             this.value = color;
27484             this.fireEvent("select", this, color);
27485         }
27486     }
27487 });/*
27488  * Based on:
27489  * Ext JS Library 1.1.1
27490  * Copyright(c) 2006-2007, Ext JS, LLC.
27491  *
27492  * Originally Released Under LGPL - original licence link has changed is not relivant.
27493  *
27494  * Fork - LGPL
27495  * <script type="text/javascript">
27496  */
27497  
27498 /**
27499  * @class Roo.DatePicker
27500  * @extends Roo.Component
27501  * Simple date picker class.
27502  * @constructor
27503  * Create a new DatePicker
27504  * @param {Object} config The config object
27505  */
27506 Roo.DatePicker = function(config){
27507     Roo.DatePicker.superclass.constructor.call(this, config);
27508
27509     this.value = config && config.value ?
27510                  config.value.clearTime() : new Date().clearTime();
27511
27512     this.addEvents({
27513         /**
27514              * @event select
27515              * Fires when a date is selected
27516              * @param {DatePicker} this
27517              * @param {Date} date The selected date
27518              */
27519         'select': true,
27520         /**
27521              * @event monthchange
27522              * Fires when the displayed month changes 
27523              * @param {DatePicker} this
27524              * @param {Date} date The selected month
27525              */
27526         'monthchange': true
27527     });
27528
27529     if(this.handler){
27530         this.on("select", this.handler,  this.scope || this);
27531     }
27532     // build the disabledDatesRE
27533     if(!this.disabledDatesRE && this.disabledDates){
27534         var dd = this.disabledDates;
27535         var re = "(?:";
27536         for(var i = 0; i < dd.length; i++){
27537             re += dd[i];
27538             if(i != dd.length-1) {
27539                 re += "|";
27540             }
27541         }
27542         this.disabledDatesRE = new RegExp(re + ")");
27543     }
27544 };
27545
27546 Roo.extend(Roo.DatePicker, Roo.Component, {
27547     /**
27548      * @cfg {String} todayText
27549      * The text to display on the button that selects the current date (defaults to "Today")
27550      */
27551     todayText : "Today",
27552     /**
27553      * @cfg {String} okText
27554      * The text to display on the ok button
27555      */
27556     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27557     /**
27558      * @cfg {String} cancelText
27559      * The text to display on the cancel button
27560      */
27561     cancelText : "Cancel",
27562     /**
27563      * @cfg {String} todayTip
27564      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27565      */
27566     todayTip : "{0} (Spacebar)",
27567     /**
27568      * @cfg {Date} minDate
27569      * Minimum allowable date (JavaScript date object, defaults to null)
27570      */
27571     minDate : null,
27572     /**
27573      * @cfg {Date} maxDate
27574      * Maximum allowable date (JavaScript date object, defaults to null)
27575      */
27576     maxDate : null,
27577     /**
27578      * @cfg {String} minText
27579      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27580      */
27581     minText : "This date is before the minimum date",
27582     /**
27583      * @cfg {String} maxText
27584      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27585      */
27586     maxText : "This date is after the maximum date",
27587     /**
27588      * @cfg {String} format
27589      * The default date format string which can be overriden for localization support.  The format must be
27590      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27591      */
27592     format : "m/d/y",
27593     /**
27594      * @cfg {Array} disabledDays
27595      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27596      */
27597     disabledDays : null,
27598     /**
27599      * @cfg {String} disabledDaysText
27600      * The tooltip to display when the date falls on a disabled day (defaults to "")
27601      */
27602     disabledDaysText : "",
27603     /**
27604      * @cfg {RegExp} disabledDatesRE
27605      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27606      */
27607     disabledDatesRE : null,
27608     /**
27609      * @cfg {String} disabledDatesText
27610      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27611      */
27612     disabledDatesText : "",
27613     /**
27614      * @cfg {Boolean} constrainToViewport
27615      * True to constrain the date picker to the viewport (defaults to true)
27616      */
27617     constrainToViewport : true,
27618     /**
27619      * @cfg {Array} monthNames
27620      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27621      */
27622     monthNames : Date.monthNames,
27623     /**
27624      * @cfg {Array} dayNames
27625      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27626      */
27627     dayNames : Date.dayNames,
27628     /**
27629      * @cfg {String} nextText
27630      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27631      */
27632     nextText: 'Next Month (Control+Right)',
27633     /**
27634      * @cfg {String} prevText
27635      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27636      */
27637     prevText: 'Previous Month (Control+Left)',
27638     /**
27639      * @cfg {String} monthYearText
27640      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27641      */
27642     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27643     /**
27644      * @cfg {Number} startDay
27645      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27646      */
27647     startDay : 0,
27648     /**
27649      * @cfg {Bool} showClear
27650      * Show a clear button (usefull for date form elements that can be blank.)
27651      */
27652     
27653     showClear: false,
27654     
27655     /**
27656      * Sets the value of the date field
27657      * @param {Date} value The date to set
27658      */
27659     setValue : function(value){
27660         var old = this.value;
27661         
27662         if (typeof(value) == 'string') {
27663          
27664             value = Date.parseDate(value, this.format);
27665         }
27666         if (!value) {
27667             value = new Date();
27668         }
27669         
27670         this.value = value.clearTime(true);
27671         if(this.el){
27672             this.update(this.value);
27673         }
27674     },
27675
27676     /**
27677      * Gets the current selected value of the date field
27678      * @return {Date} The selected date
27679      */
27680     getValue : function(){
27681         return this.value;
27682     },
27683
27684     // private
27685     focus : function(){
27686         if(this.el){
27687             this.update(this.activeDate);
27688         }
27689     },
27690
27691     // privateval
27692     onRender : function(container, position){
27693         
27694         var m = [
27695              '<table cellspacing="0">',
27696                 '<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>',
27697                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27698         var dn = this.dayNames;
27699         for(var i = 0; i < 7; i++){
27700             var d = this.startDay+i;
27701             if(d > 6){
27702                 d = d-7;
27703             }
27704             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27705         }
27706         m[m.length] = "</tr></thead><tbody><tr>";
27707         for(var i = 0; i < 42; i++) {
27708             if(i % 7 == 0 && i != 0){
27709                 m[m.length] = "</tr><tr>";
27710             }
27711             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27712         }
27713         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27714             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27715
27716         var el = document.createElement("div");
27717         el.className = "x-date-picker";
27718         el.innerHTML = m.join("");
27719
27720         container.dom.insertBefore(el, position);
27721
27722         this.el = Roo.get(el);
27723         this.eventEl = Roo.get(el.firstChild);
27724
27725         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27726             handler: this.showPrevMonth,
27727             scope: this,
27728             preventDefault:true,
27729             stopDefault:true
27730         });
27731
27732         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27733             handler: this.showNextMonth,
27734             scope: this,
27735             preventDefault:true,
27736             stopDefault:true
27737         });
27738
27739         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27740
27741         this.monthPicker = this.el.down('div.x-date-mp');
27742         this.monthPicker.enableDisplayMode('block');
27743         
27744         var kn = new Roo.KeyNav(this.eventEl, {
27745             "left" : function(e){
27746                 e.ctrlKey ?
27747                     this.showPrevMonth() :
27748                     this.update(this.activeDate.add("d", -1));
27749             },
27750
27751             "right" : function(e){
27752                 e.ctrlKey ?
27753                     this.showNextMonth() :
27754                     this.update(this.activeDate.add("d", 1));
27755             },
27756
27757             "up" : function(e){
27758                 e.ctrlKey ?
27759                     this.showNextYear() :
27760                     this.update(this.activeDate.add("d", -7));
27761             },
27762
27763             "down" : function(e){
27764                 e.ctrlKey ?
27765                     this.showPrevYear() :
27766                     this.update(this.activeDate.add("d", 7));
27767             },
27768
27769             "pageUp" : function(e){
27770                 this.showNextMonth();
27771             },
27772
27773             "pageDown" : function(e){
27774                 this.showPrevMonth();
27775             },
27776
27777             "enter" : function(e){
27778                 e.stopPropagation();
27779                 return true;
27780             },
27781
27782             scope : this
27783         });
27784
27785         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27786
27787         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27788
27789         this.el.unselectable();
27790         
27791         this.cells = this.el.select("table.x-date-inner tbody td");
27792         this.textNodes = this.el.query("table.x-date-inner tbody span");
27793
27794         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27795             text: "&#160;",
27796             tooltip: this.monthYearText
27797         });
27798
27799         this.mbtn.on('click', this.showMonthPicker, this);
27800         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27801
27802
27803         var today = (new Date()).dateFormat(this.format);
27804         
27805         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27806         if (this.showClear) {
27807             baseTb.add( new Roo.Toolbar.Fill());
27808         }
27809         baseTb.add({
27810             text: String.format(this.todayText, today),
27811             tooltip: String.format(this.todayTip, today),
27812             handler: this.selectToday,
27813             scope: this
27814         });
27815         
27816         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27817             
27818         //});
27819         if (this.showClear) {
27820             
27821             baseTb.add( new Roo.Toolbar.Fill());
27822             baseTb.add({
27823                 text: '&#160;',
27824                 cls: 'x-btn-icon x-btn-clear',
27825                 handler: function() {
27826                     //this.value = '';
27827                     this.fireEvent("select", this, '');
27828                 },
27829                 scope: this
27830             });
27831         }
27832         
27833         
27834         if(Roo.isIE){
27835             this.el.repaint();
27836         }
27837         this.update(this.value);
27838     },
27839
27840     createMonthPicker : function(){
27841         if(!this.monthPicker.dom.firstChild){
27842             var buf = ['<table border="0" cellspacing="0">'];
27843             for(var i = 0; i < 6; i++){
27844                 buf.push(
27845                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27846                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27847                     i == 0 ?
27848                     '<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>' :
27849                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27850                 );
27851             }
27852             buf.push(
27853                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27854                     this.okText,
27855                     '</button><button type="button" class="x-date-mp-cancel">',
27856                     this.cancelText,
27857                     '</button></td></tr>',
27858                 '</table>'
27859             );
27860             this.monthPicker.update(buf.join(''));
27861             this.monthPicker.on('click', this.onMonthClick, this);
27862             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27863
27864             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27865             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27866
27867             this.mpMonths.each(function(m, a, i){
27868                 i += 1;
27869                 if((i%2) == 0){
27870                     m.dom.xmonth = 5 + Math.round(i * .5);
27871                 }else{
27872                     m.dom.xmonth = Math.round((i-1) * .5);
27873                 }
27874             });
27875         }
27876     },
27877
27878     showMonthPicker : function(){
27879         this.createMonthPicker();
27880         var size = this.el.getSize();
27881         this.monthPicker.setSize(size);
27882         this.monthPicker.child('table').setSize(size);
27883
27884         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27885         this.updateMPMonth(this.mpSelMonth);
27886         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27887         this.updateMPYear(this.mpSelYear);
27888
27889         this.monthPicker.slideIn('t', {duration:.2});
27890     },
27891
27892     updateMPYear : function(y){
27893         this.mpyear = y;
27894         var ys = this.mpYears.elements;
27895         for(var i = 1; i <= 10; i++){
27896             var td = ys[i-1], y2;
27897             if((i%2) == 0){
27898                 y2 = y + Math.round(i * .5);
27899                 td.firstChild.innerHTML = y2;
27900                 td.xyear = y2;
27901             }else{
27902                 y2 = y - (5-Math.round(i * .5));
27903                 td.firstChild.innerHTML = y2;
27904                 td.xyear = y2;
27905             }
27906             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27907         }
27908     },
27909
27910     updateMPMonth : function(sm){
27911         this.mpMonths.each(function(m, a, i){
27912             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27913         });
27914     },
27915
27916     selectMPMonth: function(m){
27917         
27918     },
27919
27920     onMonthClick : function(e, t){
27921         e.stopEvent();
27922         var el = new Roo.Element(t), pn;
27923         if(el.is('button.x-date-mp-cancel')){
27924             this.hideMonthPicker();
27925         }
27926         else if(el.is('button.x-date-mp-ok')){
27927             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27928             this.hideMonthPicker();
27929         }
27930         else if(pn = el.up('td.x-date-mp-month', 2)){
27931             this.mpMonths.removeClass('x-date-mp-sel');
27932             pn.addClass('x-date-mp-sel');
27933             this.mpSelMonth = pn.dom.xmonth;
27934         }
27935         else if(pn = el.up('td.x-date-mp-year', 2)){
27936             this.mpYears.removeClass('x-date-mp-sel');
27937             pn.addClass('x-date-mp-sel');
27938             this.mpSelYear = pn.dom.xyear;
27939         }
27940         else if(el.is('a.x-date-mp-prev')){
27941             this.updateMPYear(this.mpyear-10);
27942         }
27943         else if(el.is('a.x-date-mp-next')){
27944             this.updateMPYear(this.mpyear+10);
27945         }
27946     },
27947
27948     onMonthDblClick : function(e, t){
27949         e.stopEvent();
27950         var el = new Roo.Element(t), pn;
27951         if(pn = el.up('td.x-date-mp-month', 2)){
27952             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27953             this.hideMonthPicker();
27954         }
27955         else if(pn = el.up('td.x-date-mp-year', 2)){
27956             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27957             this.hideMonthPicker();
27958         }
27959     },
27960
27961     hideMonthPicker : function(disableAnim){
27962         if(this.monthPicker){
27963             if(disableAnim === true){
27964                 this.monthPicker.hide();
27965             }else{
27966                 this.monthPicker.slideOut('t', {duration:.2});
27967             }
27968         }
27969     },
27970
27971     // private
27972     showPrevMonth : function(e){
27973         this.update(this.activeDate.add("mo", -1));
27974     },
27975
27976     // private
27977     showNextMonth : function(e){
27978         this.update(this.activeDate.add("mo", 1));
27979     },
27980
27981     // private
27982     showPrevYear : function(){
27983         this.update(this.activeDate.add("y", -1));
27984     },
27985
27986     // private
27987     showNextYear : function(){
27988         this.update(this.activeDate.add("y", 1));
27989     },
27990
27991     // private
27992     handleMouseWheel : function(e){
27993         var delta = e.getWheelDelta();
27994         if(delta > 0){
27995             this.showPrevMonth();
27996             e.stopEvent();
27997         } else if(delta < 0){
27998             this.showNextMonth();
27999             e.stopEvent();
28000         }
28001     },
28002
28003     // private
28004     handleDateClick : function(e, t){
28005         e.stopEvent();
28006         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28007             this.setValue(new Date(t.dateValue));
28008             this.fireEvent("select", this, this.value);
28009         }
28010     },
28011
28012     // private
28013     selectToday : function(){
28014         this.setValue(new Date().clearTime());
28015         this.fireEvent("select", this, this.value);
28016     },
28017
28018     // private
28019     update : function(date)
28020     {
28021         var vd = this.activeDate;
28022         this.activeDate = date;
28023         if(vd && this.el){
28024             var t = date.getTime();
28025             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28026                 this.cells.removeClass("x-date-selected");
28027                 this.cells.each(function(c){
28028                    if(c.dom.firstChild.dateValue == t){
28029                        c.addClass("x-date-selected");
28030                        setTimeout(function(){
28031                             try{c.dom.firstChild.focus();}catch(e){}
28032                        }, 50);
28033                        return false;
28034                    }
28035                 });
28036                 return;
28037             }
28038         }
28039         
28040         var days = date.getDaysInMonth();
28041         var firstOfMonth = date.getFirstDateOfMonth();
28042         var startingPos = firstOfMonth.getDay()-this.startDay;
28043
28044         if(startingPos <= this.startDay){
28045             startingPos += 7;
28046         }
28047
28048         var pm = date.add("mo", -1);
28049         var prevStart = pm.getDaysInMonth()-startingPos;
28050
28051         var cells = this.cells.elements;
28052         var textEls = this.textNodes;
28053         days += startingPos;
28054
28055         // convert everything to numbers so it's fast
28056         var day = 86400000;
28057         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28058         var today = new Date().clearTime().getTime();
28059         var sel = date.clearTime().getTime();
28060         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28061         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28062         var ddMatch = this.disabledDatesRE;
28063         var ddText = this.disabledDatesText;
28064         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28065         var ddaysText = this.disabledDaysText;
28066         var format = this.format;
28067
28068         var setCellClass = function(cal, cell){
28069             cell.title = "";
28070             var t = d.getTime();
28071             cell.firstChild.dateValue = t;
28072             if(t == today){
28073                 cell.className += " x-date-today";
28074                 cell.title = cal.todayText;
28075             }
28076             if(t == sel){
28077                 cell.className += " x-date-selected";
28078                 setTimeout(function(){
28079                     try{cell.firstChild.focus();}catch(e){}
28080                 }, 50);
28081             }
28082             // disabling
28083             if(t < min) {
28084                 cell.className = " x-date-disabled";
28085                 cell.title = cal.minText;
28086                 return;
28087             }
28088             if(t > max) {
28089                 cell.className = " x-date-disabled";
28090                 cell.title = cal.maxText;
28091                 return;
28092             }
28093             if(ddays){
28094                 if(ddays.indexOf(d.getDay()) != -1){
28095                     cell.title = ddaysText;
28096                     cell.className = " x-date-disabled";
28097                 }
28098             }
28099             if(ddMatch && format){
28100                 var fvalue = d.dateFormat(format);
28101                 if(ddMatch.test(fvalue)){
28102                     cell.title = ddText.replace("%0", fvalue);
28103                     cell.className = " x-date-disabled";
28104                 }
28105             }
28106         };
28107
28108         var i = 0;
28109         for(; i < startingPos; i++) {
28110             textEls[i].innerHTML = (++prevStart);
28111             d.setDate(d.getDate()+1);
28112             cells[i].className = "x-date-prevday";
28113             setCellClass(this, cells[i]);
28114         }
28115         for(; i < days; i++){
28116             intDay = i - startingPos + 1;
28117             textEls[i].innerHTML = (intDay);
28118             d.setDate(d.getDate()+1);
28119             cells[i].className = "x-date-active";
28120             setCellClass(this, cells[i]);
28121         }
28122         var extraDays = 0;
28123         for(; i < 42; i++) {
28124              textEls[i].innerHTML = (++extraDays);
28125              d.setDate(d.getDate()+1);
28126              cells[i].className = "x-date-nextday";
28127              setCellClass(this, cells[i]);
28128         }
28129
28130         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28131         this.fireEvent('monthchange', this, date);
28132         
28133         if(!this.internalRender){
28134             var main = this.el.dom.firstChild;
28135             var w = main.offsetWidth;
28136             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28137             Roo.fly(main).setWidth(w);
28138             this.internalRender = true;
28139             // opera does not respect the auto grow header center column
28140             // then, after it gets a width opera refuses to recalculate
28141             // without a second pass
28142             if(Roo.isOpera && !this.secondPass){
28143                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28144                 this.secondPass = true;
28145                 this.update.defer(10, this, [date]);
28146             }
28147         }
28148         
28149         
28150     }
28151 });        /*
28152  * Based on:
28153  * Ext JS Library 1.1.1
28154  * Copyright(c) 2006-2007, Ext JS, LLC.
28155  *
28156  * Originally Released Under LGPL - original licence link has changed is not relivant.
28157  *
28158  * Fork - LGPL
28159  * <script type="text/javascript">
28160  */
28161 /**
28162  * @class Roo.TabPanel
28163  * @extends Roo.util.Observable
28164  * A lightweight tab container.
28165  * <br><br>
28166  * Usage:
28167  * <pre><code>
28168 // basic tabs 1, built from existing content
28169 var tabs = new Roo.TabPanel("tabs1");
28170 tabs.addTab("script", "View Script");
28171 tabs.addTab("markup", "View Markup");
28172 tabs.activate("script");
28173
28174 // more advanced tabs, built from javascript
28175 var jtabs = new Roo.TabPanel("jtabs");
28176 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28177
28178 // set up the UpdateManager
28179 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28180 var updater = tab2.getUpdateManager();
28181 updater.setDefaultUrl("ajax1.htm");
28182 tab2.on('activate', updater.refresh, updater, true);
28183
28184 // Use setUrl for Ajax loading
28185 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28186 tab3.setUrl("ajax2.htm", null, true);
28187
28188 // Disabled tab
28189 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28190 tab4.disable();
28191
28192 jtabs.activate("jtabs-1");
28193  * </code></pre>
28194  * @constructor
28195  * Create a new TabPanel.
28196  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28197  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28198  */
28199 Roo.TabPanel = function(container, config){
28200     /**
28201     * The container element for this TabPanel.
28202     * @type Roo.Element
28203     */
28204     this.el = Roo.get(container, true);
28205     if(config){
28206         if(typeof config == "boolean"){
28207             this.tabPosition = config ? "bottom" : "top";
28208         }else{
28209             Roo.apply(this, config);
28210         }
28211     }
28212     if(this.tabPosition == "bottom"){
28213         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28214         this.el.addClass("x-tabs-bottom");
28215     }
28216     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28217     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28218     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28219     if(Roo.isIE){
28220         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28221     }
28222     if(this.tabPosition != "bottom"){
28223         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28224          * @type Roo.Element
28225          */
28226         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28227         this.el.addClass("x-tabs-top");
28228     }
28229     this.items = [];
28230
28231     this.bodyEl.setStyle("position", "relative");
28232
28233     this.active = null;
28234     this.activateDelegate = this.activate.createDelegate(this);
28235
28236     this.addEvents({
28237         /**
28238          * @event tabchange
28239          * Fires when the active tab changes
28240          * @param {Roo.TabPanel} this
28241          * @param {Roo.TabPanelItem} activePanel The new active tab
28242          */
28243         "tabchange": true,
28244         /**
28245          * @event beforetabchange
28246          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28247          * @param {Roo.TabPanel} this
28248          * @param {Object} e Set cancel to true on this object to cancel the tab change
28249          * @param {Roo.TabPanelItem} tab The tab being changed to
28250          */
28251         "beforetabchange" : true
28252     });
28253
28254     Roo.EventManager.onWindowResize(this.onResize, this);
28255     this.cpad = this.el.getPadding("lr");
28256     this.hiddenCount = 0;
28257
28258
28259     // toolbar on the tabbar support...
28260     if (this.toolbar) {
28261         var tcfg = this.toolbar;
28262         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28263         this.toolbar = new Roo.Toolbar(tcfg);
28264         if (Roo.isSafari) {
28265             var tbl = tcfg.container.child('table', true);
28266             tbl.setAttribute('width', '100%');
28267         }
28268         
28269     }
28270    
28271
28272
28273     Roo.TabPanel.superclass.constructor.call(this);
28274 };
28275
28276 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28277     /*
28278      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28279      */
28280     tabPosition : "top",
28281     /*
28282      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28283      */
28284     currentTabWidth : 0,
28285     /*
28286      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28287      */
28288     minTabWidth : 40,
28289     /*
28290      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28291      */
28292     maxTabWidth : 250,
28293     /*
28294      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28295      */
28296     preferredTabWidth : 175,
28297     /*
28298      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28299      */
28300     resizeTabs : false,
28301     /*
28302      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28303      */
28304     monitorResize : true,
28305     /*
28306      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28307      */
28308     toolbar : false,
28309
28310     /**
28311      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28312      * @param {String} id The id of the div to use <b>or create</b>
28313      * @param {String} text The text for the tab
28314      * @param {String} content (optional) Content to put in the TabPanelItem body
28315      * @param {Boolean} closable (optional) True to create a close icon on the tab
28316      * @return {Roo.TabPanelItem} The created TabPanelItem
28317      */
28318     addTab : function(id, text, content, closable){
28319         var item = new Roo.TabPanelItem(this, id, text, closable);
28320         this.addTabItem(item);
28321         if(content){
28322             item.setContent(content);
28323         }
28324         return item;
28325     },
28326
28327     /**
28328      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28329      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28330      * @return {Roo.TabPanelItem}
28331      */
28332     getTab : function(id){
28333         return this.items[id];
28334     },
28335
28336     /**
28337      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28338      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28339      */
28340     hideTab : function(id){
28341         var t = this.items[id];
28342         if(!t.isHidden()){
28343            t.setHidden(true);
28344            this.hiddenCount++;
28345            this.autoSizeTabs();
28346         }
28347     },
28348
28349     /**
28350      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28351      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28352      */
28353     unhideTab : function(id){
28354         var t = this.items[id];
28355         if(t.isHidden()){
28356            t.setHidden(false);
28357            this.hiddenCount--;
28358            this.autoSizeTabs();
28359         }
28360     },
28361
28362     /**
28363      * Adds an existing {@link Roo.TabPanelItem}.
28364      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28365      */
28366     addTabItem : function(item){
28367         this.items[item.id] = item;
28368         this.items.push(item);
28369         if(this.resizeTabs){
28370            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28371            this.autoSizeTabs();
28372         }else{
28373             item.autoSize();
28374         }
28375     },
28376
28377     /**
28378      * Removes a {@link Roo.TabPanelItem}.
28379      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28380      */
28381     removeTab : function(id){
28382         var items = this.items;
28383         var tab = items[id];
28384         if(!tab) { return; }
28385         var index = items.indexOf(tab);
28386         if(this.active == tab && items.length > 1){
28387             var newTab = this.getNextAvailable(index);
28388             if(newTab) {
28389                 newTab.activate();
28390             }
28391         }
28392         this.stripEl.dom.removeChild(tab.pnode.dom);
28393         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28394             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28395         }
28396         items.splice(index, 1);
28397         delete this.items[tab.id];
28398         tab.fireEvent("close", tab);
28399         tab.purgeListeners();
28400         this.autoSizeTabs();
28401     },
28402
28403     getNextAvailable : function(start){
28404         var items = this.items;
28405         var index = start;
28406         // look for a next tab that will slide over to
28407         // replace the one being removed
28408         while(index < items.length){
28409             var item = items[++index];
28410             if(item && !item.isHidden()){
28411                 return item;
28412             }
28413         }
28414         // if one isn't found select the previous tab (on the left)
28415         index = start;
28416         while(index >= 0){
28417             var item = items[--index];
28418             if(item && !item.isHidden()){
28419                 return item;
28420             }
28421         }
28422         return null;
28423     },
28424
28425     /**
28426      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28427      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28428      */
28429     disableTab : function(id){
28430         var tab = this.items[id];
28431         if(tab && this.active != tab){
28432             tab.disable();
28433         }
28434     },
28435
28436     /**
28437      * Enables a {@link Roo.TabPanelItem} that is disabled.
28438      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28439      */
28440     enableTab : function(id){
28441         var tab = this.items[id];
28442         tab.enable();
28443     },
28444
28445     /**
28446      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28447      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28448      * @return {Roo.TabPanelItem} The TabPanelItem.
28449      */
28450     activate : function(id){
28451         var tab = this.items[id];
28452         if(!tab){
28453             return null;
28454         }
28455         if(tab == this.active || tab.disabled){
28456             return tab;
28457         }
28458         var e = {};
28459         this.fireEvent("beforetabchange", this, e, tab);
28460         if(e.cancel !== true && !tab.disabled){
28461             if(this.active){
28462                 this.active.hide();
28463             }
28464             this.active = this.items[id];
28465             this.active.show();
28466             this.fireEvent("tabchange", this, this.active);
28467         }
28468         return tab;
28469     },
28470
28471     /**
28472      * Gets the active {@link Roo.TabPanelItem}.
28473      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28474      */
28475     getActiveTab : function(){
28476         return this.active;
28477     },
28478
28479     /**
28480      * Updates the tab body element to fit the height of the container element
28481      * for overflow scrolling
28482      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28483      */
28484     syncHeight : function(targetHeight){
28485         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28486         var bm = this.bodyEl.getMargins();
28487         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28488         this.bodyEl.setHeight(newHeight);
28489         return newHeight;
28490     },
28491
28492     onResize : function(){
28493         if(this.monitorResize){
28494             this.autoSizeTabs();
28495         }
28496     },
28497
28498     /**
28499      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28500      */
28501     beginUpdate : function(){
28502         this.updating = true;
28503     },
28504
28505     /**
28506      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28507      */
28508     endUpdate : function(){
28509         this.updating = false;
28510         this.autoSizeTabs();
28511     },
28512
28513     /**
28514      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28515      */
28516     autoSizeTabs : function(){
28517         var count = this.items.length;
28518         var vcount = count - this.hiddenCount;
28519         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28520             return;
28521         }
28522         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28523         var availWidth = Math.floor(w / vcount);
28524         var b = this.stripBody;
28525         if(b.getWidth() > w){
28526             var tabs = this.items;
28527             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28528             if(availWidth < this.minTabWidth){
28529                 /*if(!this.sleft){    // incomplete scrolling code
28530                     this.createScrollButtons();
28531                 }
28532                 this.showScroll();
28533                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28534             }
28535         }else{
28536             if(this.currentTabWidth < this.preferredTabWidth){
28537                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28538             }
28539         }
28540     },
28541
28542     /**
28543      * Returns the number of tabs in this TabPanel.
28544      * @return {Number}
28545      */
28546      getCount : function(){
28547          return this.items.length;
28548      },
28549
28550     /**
28551      * Resizes all the tabs to the passed width
28552      * @param {Number} The new width
28553      */
28554     setTabWidth : function(width){
28555         this.currentTabWidth = width;
28556         for(var i = 0, len = this.items.length; i < len; i++) {
28557                 if(!this.items[i].isHidden()) {
28558                 this.items[i].setWidth(width);
28559             }
28560         }
28561     },
28562
28563     /**
28564      * Destroys this TabPanel
28565      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28566      */
28567     destroy : function(removeEl){
28568         Roo.EventManager.removeResizeListener(this.onResize, this);
28569         for(var i = 0, len = this.items.length; i < len; i++){
28570             this.items[i].purgeListeners();
28571         }
28572         if(removeEl === true){
28573             this.el.update("");
28574             this.el.remove();
28575         }
28576     }
28577 });
28578
28579 /**
28580  * @class Roo.TabPanelItem
28581  * @extends Roo.util.Observable
28582  * Represents an individual item (tab plus body) in a TabPanel.
28583  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28584  * @param {String} id The id of this TabPanelItem
28585  * @param {String} text The text for the tab of this TabPanelItem
28586  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28587  */
28588 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28589     /**
28590      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28591      * @type Roo.TabPanel
28592      */
28593     this.tabPanel = tabPanel;
28594     /**
28595      * The id for this TabPanelItem
28596      * @type String
28597      */
28598     this.id = id;
28599     /** @private */
28600     this.disabled = false;
28601     /** @private */
28602     this.text = text;
28603     /** @private */
28604     this.loaded = false;
28605     this.closable = closable;
28606
28607     /**
28608      * The body element for this TabPanelItem.
28609      * @type Roo.Element
28610      */
28611     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28612     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28613     this.bodyEl.setStyle("display", "block");
28614     this.bodyEl.setStyle("zoom", "1");
28615     this.hideAction();
28616
28617     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28618     /** @private */
28619     this.el = Roo.get(els.el, true);
28620     this.inner = Roo.get(els.inner, true);
28621     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28622     this.pnode = Roo.get(els.el.parentNode, true);
28623     this.el.on("mousedown", this.onTabMouseDown, this);
28624     this.el.on("click", this.onTabClick, this);
28625     /** @private */
28626     if(closable){
28627         var c = Roo.get(els.close, true);
28628         c.dom.title = this.closeText;
28629         c.addClassOnOver("close-over");
28630         c.on("click", this.closeClick, this);
28631      }
28632
28633     this.addEvents({
28634          /**
28635          * @event activate
28636          * Fires when this tab becomes the active tab.
28637          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28638          * @param {Roo.TabPanelItem} this
28639          */
28640         "activate": true,
28641         /**
28642          * @event beforeclose
28643          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28644          * @param {Roo.TabPanelItem} this
28645          * @param {Object} e Set cancel to true on this object to cancel the close.
28646          */
28647         "beforeclose": true,
28648         /**
28649          * @event close
28650          * Fires when this tab is closed.
28651          * @param {Roo.TabPanelItem} this
28652          */
28653          "close": true,
28654         /**
28655          * @event deactivate
28656          * Fires when this tab is no longer the active tab.
28657          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28658          * @param {Roo.TabPanelItem} this
28659          */
28660          "deactivate" : true
28661     });
28662     this.hidden = false;
28663
28664     Roo.TabPanelItem.superclass.constructor.call(this);
28665 };
28666
28667 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28668     purgeListeners : function(){
28669        Roo.util.Observable.prototype.purgeListeners.call(this);
28670        this.el.removeAllListeners();
28671     },
28672     /**
28673      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28674      */
28675     show : function(){
28676         this.pnode.addClass("on");
28677         this.showAction();
28678         if(Roo.isOpera){
28679             this.tabPanel.stripWrap.repaint();
28680         }
28681         this.fireEvent("activate", this.tabPanel, this);
28682     },
28683
28684     /**
28685      * Returns true if this tab is the active tab.
28686      * @return {Boolean}
28687      */
28688     isActive : function(){
28689         return this.tabPanel.getActiveTab() == this;
28690     },
28691
28692     /**
28693      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28694      */
28695     hide : function(){
28696         this.pnode.removeClass("on");
28697         this.hideAction();
28698         this.fireEvent("deactivate", this.tabPanel, this);
28699     },
28700
28701     hideAction : function(){
28702         this.bodyEl.hide();
28703         this.bodyEl.setStyle("position", "absolute");
28704         this.bodyEl.setLeft("-20000px");
28705         this.bodyEl.setTop("-20000px");
28706     },
28707
28708     showAction : function(){
28709         this.bodyEl.setStyle("position", "relative");
28710         this.bodyEl.setTop("");
28711         this.bodyEl.setLeft("");
28712         this.bodyEl.show();
28713     },
28714
28715     /**
28716      * Set the tooltip for the tab.
28717      * @param {String} tooltip The tab's tooltip
28718      */
28719     setTooltip : function(text){
28720         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28721             this.textEl.dom.qtip = text;
28722             this.textEl.dom.removeAttribute('title');
28723         }else{
28724             this.textEl.dom.title = text;
28725         }
28726     },
28727
28728     onTabClick : function(e){
28729         e.preventDefault();
28730         this.tabPanel.activate(this.id);
28731     },
28732
28733     onTabMouseDown : function(e){
28734         e.preventDefault();
28735         this.tabPanel.activate(this.id);
28736     },
28737
28738     getWidth : function(){
28739         return this.inner.getWidth();
28740     },
28741
28742     setWidth : function(width){
28743         var iwidth = width - this.pnode.getPadding("lr");
28744         this.inner.setWidth(iwidth);
28745         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28746         this.pnode.setWidth(width);
28747     },
28748
28749     /**
28750      * Show or hide the tab
28751      * @param {Boolean} hidden True to hide or false to show.
28752      */
28753     setHidden : function(hidden){
28754         this.hidden = hidden;
28755         this.pnode.setStyle("display", hidden ? "none" : "");
28756     },
28757
28758     /**
28759      * Returns true if this tab is "hidden"
28760      * @return {Boolean}
28761      */
28762     isHidden : function(){
28763         return this.hidden;
28764     },
28765
28766     /**
28767      * Returns the text for this tab
28768      * @return {String}
28769      */
28770     getText : function(){
28771         return this.text;
28772     },
28773
28774     autoSize : function(){
28775         //this.el.beginMeasure();
28776         this.textEl.setWidth(1);
28777         /*
28778          *  #2804 [new] Tabs in Roojs
28779          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28780          */
28781         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28782         //this.el.endMeasure();
28783     },
28784
28785     /**
28786      * Sets the text for the tab (Note: this also sets the tooltip text)
28787      * @param {String} text The tab's text and tooltip
28788      */
28789     setText : function(text){
28790         this.text = text;
28791         this.textEl.update(text);
28792         this.setTooltip(text);
28793         if(!this.tabPanel.resizeTabs){
28794             this.autoSize();
28795         }
28796     },
28797     /**
28798      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28799      */
28800     activate : function(){
28801         this.tabPanel.activate(this.id);
28802     },
28803
28804     /**
28805      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28806      */
28807     disable : function(){
28808         if(this.tabPanel.active != this){
28809             this.disabled = true;
28810             this.pnode.addClass("disabled");
28811         }
28812     },
28813
28814     /**
28815      * Enables this TabPanelItem if it was previously disabled.
28816      */
28817     enable : function(){
28818         this.disabled = false;
28819         this.pnode.removeClass("disabled");
28820     },
28821
28822     /**
28823      * Sets the content for this TabPanelItem.
28824      * @param {String} content The content
28825      * @param {Boolean} loadScripts true to look for and load scripts
28826      */
28827     setContent : function(content, loadScripts){
28828         this.bodyEl.update(content, loadScripts);
28829     },
28830
28831     /**
28832      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28833      * @return {Roo.UpdateManager} The UpdateManager
28834      */
28835     getUpdateManager : function(){
28836         return this.bodyEl.getUpdateManager();
28837     },
28838
28839     /**
28840      * Set a URL to be used to load the content for this TabPanelItem.
28841      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28842      * @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)
28843      * @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)
28844      * @return {Roo.UpdateManager} The UpdateManager
28845      */
28846     setUrl : function(url, params, loadOnce){
28847         if(this.refreshDelegate){
28848             this.un('activate', this.refreshDelegate);
28849         }
28850         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28851         this.on("activate", this.refreshDelegate);
28852         return this.bodyEl.getUpdateManager();
28853     },
28854
28855     /** @private */
28856     _handleRefresh : function(url, params, loadOnce){
28857         if(!loadOnce || !this.loaded){
28858             var updater = this.bodyEl.getUpdateManager();
28859             updater.update(url, params, this._setLoaded.createDelegate(this));
28860         }
28861     },
28862
28863     /**
28864      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28865      *   Will fail silently if the setUrl method has not been called.
28866      *   This does not activate the panel, just updates its content.
28867      */
28868     refresh : function(){
28869         if(this.refreshDelegate){
28870            this.loaded = false;
28871            this.refreshDelegate();
28872         }
28873     },
28874
28875     /** @private */
28876     _setLoaded : function(){
28877         this.loaded = true;
28878     },
28879
28880     /** @private */
28881     closeClick : function(e){
28882         var o = {};
28883         e.stopEvent();
28884         this.fireEvent("beforeclose", this, o);
28885         if(o.cancel !== true){
28886             this.tabPanel.removeTab(this.id);
28887         }
28888     },
28889     /**
28890      * The text displayed in the tooltip for the close icon.
28891      * @type String
28892      */
28893     closeText : "Close this tab"
28894 });
28895
28896 /** @private */
28897 Roo.TabPanel.prototype.createStrip = function(container){
28898     var strip = document.createElement("div");
28899     strip.className = "x-tabs-wrap";
28900     container.appendChild(strip);
28901     return strip;
28902 };
28903 /** @private */
28904 Roo.TabPanel.prototype.createStripList = function(strip){
28905     // div wrapper for retard IE
28906     // returns the "tr" element.
28907     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28908         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28909         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28910     return strip.firstChild.firstChild.firstChild.firstChild;
28911 };
28912 /** @private */
28913 Roo.TabPanel.prototype.createBody = function(container){
28914     var body = document.createElement("div");
28915     Roo.id(body, "tab-body");
28916     Roo.fly(body).addClass("x-tabs-body");
28917     container.appendChild(body);
28918     return body;
28919 };
28920 /** @private */
28921 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28922     var body = Roo.getDom(id);
28923     if(!body){
28924         body = document.createElement("div");
28925         body.id = id;
28926     }
28927     Roo.fly(body).addClass("x-tabs-item-body");
28928     bodyEl.insertBefore(body, bodyEl.firstChild);
28929     return body;
28930 };
28931 /** @private */
28932 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28933     var td = document.createElement("td");
28934     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28935     //stripEl.appendChild(td);
28936     if(closable){
28937         td.className = "x-tabs-closable";
28938         if(!this.closeTpl){
28939             this.closeTpl = new Roo.Template(
28940                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28941                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28942                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28943             );
28944         }
28945         var el = this.closeTpl.overwrite(td, {"text": text});
28946         var close = el.getElementsByTagName("div")[0];
28947         var inner = el.getElementsByTagName("em")[0];
28948         return {"el": el, "close": close, "inner": inner};
28949     } else {
28950         if(!this.tabTpl){
28951             this.tabTpl = new Roo.Template(
28952                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28953                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28954             );
28955         }
28956         var el = this.tabTpl.overwrite(td, {"text": text});
28957         var inner = el.getElementsByTagName("em")[0];
28958         return {"el": el, "inner": inner};
28959     }
28960 };/*
28961  * Based on:
28962  * Ext JS Library 1.1.1
28963  * Copyright(c) 2006-2007, Ext JS, LLC.
28964  *
28965  * Originally Released Under LGPL - original licence link has changed is not relivant.
28966  *
28967  * Fork - LGPL
28968  * <script type="text/javascript">
28969  */
28970
28971 /**
28972  * @class Roo.Button
28973  * @extends Roo.util.Observable
28974  * Simple Button class
28975  * @cfg {String} text The button text
28976  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28977  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28978  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28979  * @cfg {Object} scope The scope of the handler
28980  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28981  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28982  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28983  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28984  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28985  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28986    applies if enableToggle = true)
28987  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28988  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28989   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28990  * @constructor
28991  * Create a new button
28992  * @param {Object} config The config object
28993  */
28994 Roo.Button = function(renderTo, config)
28995 {
28996     if (!config) {
28997         config = renderTo;
28998         renderTo = config.renderTo || false;
28999     }
29000     
29001     Roo.apply(this, config);
29002     this.addEvents({
29003         /**
29004              * @event click
29005              * Fires when this button is clicked
29006              * @param {Button} this
29007              * @param {EventObject} e The click event
29008              */
29009             "click" : true,
29010         /**
29011              * @event toggle
29012              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29013              * @param {Button} this
29014              * @param {Boolean} pressed
29015              */
29016             "toggle" : true,
29017         /**
29018              * @event mouseover
29019              * Fires when the mouse hovers over the button
29020              * @param {Button} this
29021              * @param {Event} e The event object
29022              */
29023         'mouseover' : true,
29024         /**
29025              * @event mouseout
29026              * Fires when the mouse exits the button
29027              * @param {Button} this
29028              * @param {Event} e The event object
29029              */
29030         'mouseout': true,
29031          /**
29032              * @event render
29033              * Fires when the button is rendered
29034              * @param {Button} this
29035              */
29036         'render': true
29037     });
29038     if(this.menu){
29039         this.menu = Roo.menu.MenuMgr.get(this.menu);
29040     }
29041     // register listeners first!!  - so render can be captured..
29042     Roo.util.Observable.call(this);
29043     if(renderTo){
29044         this.render(renderTo);
29045     }
29046     
29047   
29048 };
29049
29050 Roo.extend(Roo.Button, Roo.util.Observable, {
29051     /**
29052      * 
29053      */
29054     
29055     /**
29056      * Read-only. True if this button is hidden
29057      * @type Boolean
29058      */
29059     hidden : false,
29060     /**
29061      * Read-only. True if this button is disabled
29062      * @type Boolean
29063      */
29064     disabled : false,
29065     /**
29066      * Read-only. True if this button is pressed (only if enableToggle = true)
29067      * @type Boolean
29068      */
29069     pressed : false,
29070
29071     /**
29072      * @cfg {Number} tabIndex 
29073      * The DOM tabIndex for this button (defaults to undefined)
29074      */
29075     tabIndex : undefined,
29076
29077     /**
29078      * @cfg {Boolean} enableToggle
29079      * True to enable pressed/not pressed toggling (defaults to false)
29080      */
29081     enableToggle: false,
29082     /**
29083      * @cfg {Mixed} menu
29084      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29085      */
29086     menu : undefined,
29087     /**
29088      * @cfg {String} menuAlign
29089      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29090      */
29091     menuAlign : "tl-bl?",
29092
29093     /**
29094      * @cfg {String} iconCls
29095      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29096      */
29097     iconCls : undefined,
29098     /**
29099      * @cfg {String} type
29100      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29101      */
29102     type : 'button',
29103
29104     // private
29105     menuClassTarget: 'tr',
29106
29107     /**
29108      * @cfg {String} clickEvent
29109      * The type of event to map to the button's event handler (defaults to 'click')
29110      */
29111     clickEvent : 'click',
29112
29113     /**
29114      * @cfg {Boolean} handleMouseEvents
29115      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29116      */
29117     handleMouseEvents : true,
29118
29119     /**
29120      * @cfg {String} tooltipType
29121      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29122      */
29123     tooltipType : 'qtip',
29124
29125     /**
29126      * @cfg {String} cls
29127      * A CSS class to apply to the button's main element.
29128      */
29129     
29130     /**
29131      * @cfg {Roo.Template} template (Optional)
29132      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29133      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29134      * require code modifications if required elements (e.g. a button) aren't present.
29135      */
29136
29137     // private
29138     render : function(renderTo){
29139         var btn;
29140         if(this.hideParent){
29141             this.parentEl = Roo.get(renderTo);
29142         }
29143         if(!this.dhconfig){
29144             if(!this.template){
29145                 if(!Roo.Button.buttonTemplate){
29146                     // hideous table template
29147                     Roo.Button.buttonTemplate = new Roo.Template(
29148                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29149                         '<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>',
29150                         "</tr></tbody></table>");
29151                 }
29152                 this.template = Roo.Button.buttonTemplate;
29153             }
29154             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29155             var btnEl = btn.child("button:first");
29156             btnEl.on('focus', this.onFocus, this);
29157             btnEl.on('blur', this.onBlur, this);
29158             if(this.cls){
29159                 btn.addClass(this.cls);
29160             }
29161             if(this.icon){
29162                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29163             }
29164             if(this.iconCls){
29165                 btnEl.addClass(this.iconCls);
29166                 if(!this.cls){
29167                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29168                 }
29169             }
29170             if(this.tabIndex !== undefined){
29171                 btnEl.dom.tabIndex = this.tabIndex;
29172             }
29173             if(this.tooltip){
29174                 if(typeof this.tooltip == 'object'){
29175                     Roo.QuickTips.tips(Roo.apply({
29176                           target: btnEl.id
29177                     }, this.tooltip));
29178                 } else {
29179                     btnEl.dom[this.tooltipType] = this.tooltip;
29180                 }
29181             }
29182         }else{
29183             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29184         }
29185         this.el = btn;
29186         if(this.id){
29187             this.el.dom.id = this.el.id = this.id;
29188         }
29189         if(this.menu){
29190             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29191             this.menu.on("show", this.onMenuShow, this);
29192             this.menu.on("hide", this.onMenuHide, this);
29193         }
29194         btn.addClass("x-btn");
29195         if(Roo.isIE && !Roo.isIE7){
29196             this.autoWidth.defer(1, this);
29197         }else{
29198             this.autoWidth();
29199         }
29200         if(this.handleMouseEvents){
29201             btn.on("mouseover", this.onMouseOver, this);
29202             btn.on("mouseout", this.onMouseOut, this);
29203             btn.on("mousedown", this.onMouseDown, this);
29204         }
29205         btn.on(this.clickEvent, this.onClick, this);
29206         //btn.on("mouseup", this.onMouseUp, this);
29207         if(this.hidden){
29208             this.hide();
29209         }
29210         if(this.disabled){
29211             this.disable();
29212         }
29213         Roo.ButtonToggleMgr.register(this);
29214         if(this.pressed){
29215             this.el.addClass("x-btn-pressed");
29216         }
29217         if(this.repeat){
29218             var repeater = new Roo.util.ClickRepeater(btn,
29219                 typeof this.repeat == "object" ? this.repeat : {}
29220             );
29221             repeater.on("click", this.onClick,  this);
29222         }
29223         
29224         this.fireEvent('render', this);
29225         
29226     },
29227     /**
29228      * Returns the button's underlying element
29229      * @return {Roo.Element} The element
29230      */
29231     getEl : function(){
29232         return this.el;  
29233     },
29234     
29235     /**
29236      * Destroys this Button and removes any listeners.
29237      */
29238     destroy : function(){
29239         Roo.ButtonToggleMgr.unregister(this);
29240         this.el.removeAllListeners();
29241         this.purgeListeners();
29242         this.el.remove();
29243     },
29244
29245     // private
29246     autoWidth : function(){
29247         if(this.el){
29248             this.el.setWidth("auto");
29249             if(Roo.isIE7 && Roo.isStrict){
29250                 var ib = this.el.child('button');
29251                 if(ib && ib.getWidth() > 20){
29252                     ib.clip();
29253                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29254                 }
29255             }
29256             if(this.minWidth){
29257                 if(this.hidden){
29258                     this.el.beginMeasure();
29259                 }
29260                 if(this.el.getWidth() < this.minWidth){
29261                     this.el.setWidth(this.minWidth);
29262                 }
29263                 if(this.hidden){
29264                     this.el.endMeasure();
29265                 }
29266             }
29267         }
29268     },
29269
29270     /**
29271      * Assigns this button's click handler
29272      * @param {Function} handler The function to call when the button is clicked
29273      * @param {Object} scope (optional) Scope for the function passed in
29274      */
29275     setHandler : function(handler, scope){
29276         this.handler = handler;
29277         this.scope = scope;  
29278     },
29279     
29280     /**
29281      * Sets this button's text
29282      * @param {String} text The button text
29283      */
29284     setText : function(text){
29285         this.text = text;
29286         if(this.el){
29287             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29288         }
29289         this.autoWidth();
29290     },
29291     
29292     /**
29293      * Gets the text for this button
29294      * @return {String} The button text
29295      */
29296     getText : function(){
29297         return this.text;  
29298     },
29299     
29300     /**
29301      * Show this button
29302      */
29303     show: function(){
29304         this.hidden = false;
29305         if(this.el){
29306             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29307         }
29308     },
29309     
29310     /**
29311      * Hide this button
29312      */
29313     hide: function(){
29314         this.hidden = true;
29315         if(this.el){
29316             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29317         }
29318     },
29319     
29320     /**
29321      * Convenience function for boolean show/hide
29322      * @param {Boolean} visible True to show, false to hide
29323      */
29324     setVisible: function(visible){
29325         if(visible) {
29326             this.show();
29327         }else{
29328             this.hide();
29329         }
29330     },
29331     
29332     /**
29333      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29334      * @param {Boolean} state (optional) Force a particular state
29335      */
29336     toggle : function(state){
29337         state = state === undefined ? !this.pressed : state;
29338         if(state != this.pressed){
29339             if(state){
29340                 this.el.addClass("x-btn-pressed");
29341                 this.pressed = true;
29342                 this.fireEvent("toggle", this, true);
29343             }else{
29344                 this.el.removeClass("x-btn-pressed");
29345                 this.pressed = false;
29346                 this.fireEvent("toggle", this, false);
29347             }
29348             if(this.toggleHandler){
29349                 this.toggleHandler.call(this.scope || this, this, state);
29350             }
29351         }
29352     },
29353     
29354     /**
29355      * Focus the button
29356      */
29357     focus : function(){
29358         this.el.child('button:first').focus();
29359     },
29360     
29361     /**
29362      * Disable this button
29363      */
29364     disable : function(){
29365         if(this.el){
29366             this.el.addClass("x-btn-disabled");
29367         }
29368         this.disabled = true;
29369     },
29370     
29371     /**
29372      * Enable this button
29373      */
29374     enable : function(){
29375         if(this.el){
29376             this.el.removeClass("x-btn-disabled");
29377         }
29378         this.disabled = false;
29379     },
29380
29381     /**
29382      * Convenience function for boolean enable/disable
29383      * @param {Boolean} enabled True to enable, false to disable
29384      */
29385     setDisabled : function(v){
29386         this[v !== true ? "enable" : "disable"]();
29387     },
29388
29389     // private
29390     onClick : function(e)
29391     {
29392         if(e){
29393             e.preventDefault();
29394         }
29395         if(e.button != 0){
29396             return;
29397         }
29398         if(!this.disabled){
29399             if(this.enableToggle){
29400                 this.toggle();
29401             }
29402             if(this.menu && !this.menu.isVisible()){
29403                 this.menu.show(this.el, this.menuAlign);
29404             }
29405             this.fireEvent("click", this, e);
29406             if(this.handler){
29407                 this.el.removeClass("x-btn-over");
29408                 this.handler.call(this.scope || this, this, e);
29409             }
29410         }
29411     },
29412     // private
29413     onMouseOver : function(e){
29414         if(!this.disabled){
29415             this.el.addClass("x-btn-over");
29416             this.fireEvent('mouseover', this, e);
29417         }
29418     },
29419     // private
29420     onMouseOut : function(e){
29421         if(!e.within(this.el,  true)){
29422             this.el.removeClass("x-btn-over");
29423             this.fireEvent('mouseout', this, e);
29424         }
29425     },
29426     // private
29427     onFocus : function(e){
29428         if(!this.disabled){
29429             this.el.addClass("x-btn-focus");
29430         }
29431     },
29432     // private
29433     onBlur : function(e){
29434         this.el.removeClass("x-btn-focus");
29435     },
29436     // private
29437     onMouseDown : function(e){
29438         if(!this.disabled && e.button == 0){
29439             this.el.addClass("x-btn-click");
29440             Roo.get(document).on('mouseup', this.onMouseUp, this);
29441         }
29442     },
29443     // private
29444     onMouseUp : function(e){
29445         if(e.button == 0){
29446             this.el.removeClass("x-btn-click");
29447             Roo.get(document).un('mouseup', this.onMouseUp, this);
29448         }
29449     },
29450     // private
29451     onMenuShow : function(e){
29452         this.el.addClass("x-btn-menu-active");
29453     },
29454     // private
29455     onMenuHide : function(e){
29456         this.el.removeClass("x-btn-menu-active");
29457     }   
29458 });
29459
29460 // Private utility class used by Button
29461 Roo.ButtonToggleMgr = function(){
29462    var groups = {};
29463    
29464    function toggleGroup(btn, state){
29465        if(state){
29466            var g = groups[btn.toggleGroup];
29467            for(var i = 0, l = g.length; i < l; i++){
29468                if(g[i] != btn){
29469                    g[i].toggle(false);
29470                }
29471            }
29472        }
29473    }
29474    
29475    return {
29476        register : function(btn){
29477            if(!btn.toggleGroup){
29478                return;
29479            }
29480            var g = groups[btn.toggleGroup];
29481            if(!g){
29482                g = groups[btn.toggleGroup] = [];
29483            }
29484            g.push(btn);
29485            btn.on("toggle", toggleGroup);
29486        },
29487        
29488        unregister : function(btn){
29489            if(!btn.toggleGroup){
29490                return;
29491            }
29492            var g = groups[btn.toggleGroup];
29493            if(g){
29494                g.remove(btn);
29495                btn.un("toggle", toggleGroup);
29496            }
29497        }
29498    };
29499 }();/*
29500  * Based on:
29501  * Ext JS Library 1.1.1
29502  * Copyright(c) 2006-2007, Ext JS, LLC.
29503  *
29504  * Originally Released Under LGPL - original licence link has changed is not relivant.
29505  *
29506  * Fork - LGPL
29507  * <script type="text/javascript">
29508  */
29509  
29510 /**
29511  * @class Roo.SplitButton
29512  * @extends Roo.Button
29513  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29514  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29515  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29516  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29517  * @cfg {String} arrowTooltip The title attribute of the arrow
29518  * @constructor
29519  * Create a new menu button
29520  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29521  * @param {Object} config The config object
29522  */
29523 Roo.SplitButton = function(renderTo, config){
29524     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29525     /**
29526      * @event arrowclick
29527      * Fires when this button's arrow is clicked
29528      * @param {SplitButton} this
29529      * @param {EventObject} e The click event
29530      */
29531     this.addEvents({"arrowclick":true});
29532 };
29533
29534 Roo.extend(Roo.SplitButton, Roo.Button, {
29535     render : function(renderTo){
29536         // this is one sweet looking template!
29537         var tpl = new Roo.Template(
29538             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29539             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29540             '<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>',
29541             "</tbody></table></td><td>",
29542             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29543             '<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>',
29544             "</tbody></table></td></tr></table>"
29545         );
29546         var btn = tpl.append(renderTo, [this.text, this.type], true);
29547         var btnEl = btn.child("button");
29548         if(this.cls){
29549             btn.addClass(this.cls);
29550         }
29551         if(this.icon){
29552             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29553         }
29554         if(this.iconCls){
29555             btnEl.addClass(this.iconCls);
29556             if(!this.cls){
29557                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29558             }
29559         }
29560         this.el = btn;
29561         if(this.handleMouseEvents){
29562             btn.on("mouseover", this.onMouseOver, this);
29563             btn.on("mouseout", this.onMouseOut, this);
29564             btn.on("mousedown", this.onMouseDown, this);
29565             btn.on("mouseup", this.onMouseUp, this);
29566         }
29567         btn.on(this.clickEvent, this.onClick, this);
29568         if(this.tooltip){
29569             if(typeof this.tooltip == 'object'){
29570                 Roo.QuickTips.tips(Roo.apply({
29571                       target: btnEl.id
29572                 }, this.tooltip));
29573             } else {
29574                 btnEl.dom[this.tooltipType] = this.tooltip;
29575             }
29576         }
29577         if(this.arrowTooltip){
29578             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29579         }
29580         if(this.hidden){
29581             this.hide();
29582         }
29583         if(this.disabled){
29584             this.disable();
29585         }
29586         if(this.pressed){
29587             this.el.addClass("x-btn-pressed");
29588         }
29589         if(Roo.isIE && !Roo.isIE7){
29590             this.autoWidth.defer(1, this);
29591         }else{
29592             this.autoWidth();
29593         }
29594         if(this.menu){
29595             this.menu.on("show", this.onMenuShow, this);
29596             this.menu.on("hide", this.onMenuHide, this);
29597         }
29598         this.fireEvent('render', this);
29599     },
29600
29601     // private
29602     autoWidth : function(){
29603         if(this.el){
29604             var tbl = this.el.child("table:first");
29605             var tbl2 = this.el.child("table:last");
29606             this.el.setWidth("auto");
29607             tbl.setWidth("auto");
29608             if(Roo.isIE7 && Roo.isStrict){
29609                 var ib = this.el.child('button:first');
29610                 if(ib && ib.getWidth() > 20){
29611                     ib.clip();
29612                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29613                 }
29614             }
29615             if(this.minWidth){
29616                 if(this.hidden){
29617                     this.el.beginMeasure();
29618                 }
29619                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29620                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29621                 }
29622                 if(this.hidden){
29623                     this.el.endMeasure();
29624                 }
29625             }
29626             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29627         } 
29628     },
29629     /**
29630      * Sets this button's click handler
29631      * @param {Function} handler The function to call when the button is clicked
29632      * @param {Object} scope (optional) Scope for the function passed above
29633      */
29634     setHandler : function(handler, scope){
29635         this.handler = handler;
29636         this.scope = scope;  
29637     },
29638     
29639     /**
29640      * Sets this button's arrow click handler
29641      * @param {Function} handler The function to call when the arrow is clicked
29642      * @param {Object} scope (optional) Scope for the function passed above
29643      */
29644     setArrowHandler : function(handler, scope){
29645         this.arrowHandler = handler;
29646         this.scope = scope;  
29647     },
29648     
29649     /**
29650      * Focus the button
29651      */
29652     focus : function(){
29653         if(this.el){
29654             this.el.child("button:first").focus();
29655         }
29656     },
29657
29658     // private
29659     onClick : function(e){
29660         e.preventDefault();
29661         if(!this.disabled){
29662             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29663                 if(this.menu && !this.menu.isVisible()){
29664                     this.menu.show(this.el, this.menuAlign);
29665                 }
29666                 this.fireEvent("arrowclick", this, e);
29667                 if(this.arrowHandler){
29668                     this.arrowHandler.call(this.scope || this, this, e);
29669                 }
29670             }else{
29671                 this.fireEvent("click", this, e);
29672                 if(this.handler){
29673                     this.handler.call(this.scope || this, this, e);
29674                 }
29675             }
29676         }
29677     },
29678     // private
29679     onMouseDown : function(e){
29680         if(!this.disabled){
29681             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29682         }
29683     },
29684     // private
29685     onMouseUp : function(e){
29686         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29687     }   
29688 });
29689
29690
29691 // backwards compat
29692 Roo.MenuButton = Roo.SplitButton;/*
29693  * Based on:
29694  * Ext JS Library 1.1.1
29695  * Copyright(c) 2006-2007, Ext JS, LLC.
29696  *
29697  * Originally Released Under LGPL - original licence link has changed is not relivant.
29698  *
29699  * Fork - LGPL
29700  * <script type="text/javascript">
29701  */
29702
29703 /**
29704  * @class Roo.Toolbar
29705  * Basic Toolbar class.
29706  * @constructor
29707  * Creates a new Toolbar
29708  * @param {Object} container The config object
29709  */ 
29710 Roo.Toolbar = function(container, buttons, config)
29711 {
29712     /// old consturctor format still supported..
29713     if(container instanceof Array){ // omit the container for later rendering
29714         buttons = container;
29715         config = buttons;
29716         container = null;
29717     }
29718     if (typeof(container) == 'object' && container.xtype) {
29719         config = container;
29720         container = config.container;
29721         buttons = config.buttons || []; // not really - use items!!
29722     }
29723     var xitems = [];
29724     if (config && config.items) {
29725         xitems = config.items;
29726         delete config.items;
29727     }
29728     Roo.apply(this, config);
29729     this.buttons = buttons;
29730     
29731     if(container){
29732         this.render(container);
29733     }
29734     this.xitems = xitems;
29735     Roo.each(xitems, function(b) {
29736         this.add(b);
29737     }, this);
29738     
29739 };
29740
29741 Roo.Toolbar.prototype = {
29742     /**
29743      * @cfg {Array} items
29744      * array of button configs or elements to add (will be converted to a MixedCollection)
29745      */
29746     
29747     /**
29748      * @cfg {String/HTMLElement/Element} container
29749      * The id or element that will contain the toolbar
29750      */
29751     // private
29752     render : function(ct){
29753         this.el = Roo.get(ct);
29754         if(this.cls){
29755             this.el.addClass(this.cls);
29756         }
29757         // using a table allows for vertical alignment
29758         // 100% width is needed by Safari...
29759         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29760         this.tr = this.el.child("tr", true);
29761         var autoId = 0;
29762         this.items = new Roo.util.MixedCollection(false, function(o){
29763             return o.id || ("item" + (++autoId));
29764         });
29765         if(this.buttons){
29766             this.add.apply(this, this.buttons);
29767             delete this.buttons;
29768         }
29769     },
29770
29771     /**
29772      * Adds element(s) to the toolbar -- this function takes a variable number of 
29773      * arguments of mixed type and adds them to the toolbar.
29774      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29775      * <ul>
29776      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29777      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29778      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29779      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29780      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29781      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29782      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29783      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29784      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29785      * </ul>
29786      * @param {Mixed} arg2
29787      * @param {Mixed} etc.
29788      */
29789     add : function(){
29790         var a = arguments, l = a.length;
29791         for(var i = 0; i < l; i++){
29792             this._add(a[i]);
29793         }
29794     },
29795     // private..
29796     _add : function(el) {
29797         
29798         if (el.xtype) {
29799             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29800         }
29801         
29802         if (el.applyTo){ // some kind of form field
29803             return this.addField(el);
29804         } 
29805         if (el.render){ // some kind of Toolbar.Item
29806             return this.addItem(el);
29807         }
29808         if (typeof el == "string"){ // string
29809             if(el == "separator" || el == "-"){
29810                 return this.addSeparator();
29811             }
29812             if (el == " "){
29813                 return this.addSpacer();
29814             }
29815             if(el == "->"){
29816                 return this.addFill();
29817             }
29818             return this.addText(el);
29819             
29820         }
29821         if(el.tagName){ // element
29822             return this.addElement(el);
29823         }
29824         if(typeof el == "object"){ // must be button config?
29825             return this.addButton(el);
29826         }
29827         // and now what?!?!
29828         return false;
29829         
29830     },
29831     
29832     /**
29833      * Add an Xtype element
29834      * @param {Object} xtype Xtype Object
29835      * @return {Object} created Object
29836      */
29837     addxtype : function(e){
29838         return this.add(e);  
29839     },
29840     
29841     /**
29842      * Returns the Element for this toolbar.
29843      * @return {Roo.Element}
29844      */
29845     getEl : function(){
29846         return this.el;  
29847     },
29848     
29849     /**
29850      * Adds a separator
29851      * @return {Roo.Toolbar.Item} The separator item
29852      */
29853     addSeparator : function(){
29854         return this.addItem(new Roo.Toolbar.Separator());
29855     },
29856
29857     /**
29858      * Adds a spacer element
29859      * @return {Roo.Toolbar.Spacer} The spacer item
29860      */
29861     addSpacer : function(){
29862         return this.addItem(new Roo.Toolbar.Spacer());
29863     },
29864
29865     /**
29866      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29867      * @return {Roo.Toolbar.Fill} The fill item
29868      */
29869     addFill : function(){
29870         return this.addItem(new Roo.Toolbar.Fill());
29871     },
29872
29873     /**
29874      * Adds any standard HTML element to the toolbar
29875      * @param {String/HTMLElement/Element} el The element or id of the element to add
29876      * @return {Roo.Toolbar.Item} The element's item
29877      */
29878     addElement : function(el){
29879         return this.addItem(new Roo.Toolbar.Item(el));
29880     },
29881     /**
29882      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29883      * @type Roo.util.MixedCollection  
29884      */
29885     items : false,
29886      
29887     /**
29888      * Adds any Toolbar.Item or subclass
29889      * @param {Roo.Toolbar.Item} item
29890      * @return {Roo.Toolbar.Item} The item
29891      */
29892     addItem : function(item){
29893         var td = this.nextBlock();
29894         item.render(td);
29895         this.items.add(item);
29896         return item;
29897     },
29898     
29899     /**
29900      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29901      * @param {Object/Array} config A button config or array of configs
29902      * @return {Roo.Toolbar.Button/Array}
29903      */
29904     addButton : function(config){
29905         if(config instanceof Array){
29906             var buttons = [];
29907             for(var i = 0, len = config.length; i < len; i++) {
29908                 buttons.push(this.addButton(config[i]));
29909             }
29910             return buttons;
29911         }
29912         var b = config;
29913         if(!(config instanceof Roo.Toolbar.Button)){
29914             b = config.split ?
29915                 new Roo.Toolbar.SplitButton(config) :
29916                 new Roo.Toolbar.Button(config);
29917         }
29918         var td = this.nextBlock();
29919         b.render(td);
29920         this.items.add(b);
29921         return b;
29922     },
29923     
29924     /**
29925      * Adds text to the toolbar
29926      * @param {String} text The text to add
29927      * @return {Roo.Toolbar.Item} The element's item
29928      */
29929     addText : function(text){
29930         return this.addItem(new Roo.Toolbar.TextItem(text));
29931     },
29932     
29933     /**
29934      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29935      * @param {Number} index The index where the item is to be inserted
29936      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29937      * @return {Roo.Toolbar.Button/Item}
29938      */
29939     insertButton : function(index, item){
29940         if(item instanceof Array){
29941             var buttons = [];
29942             for(var i = 0, len = item.length; i < len; i++) {
29943                buttons.push(this.insertButton(index + i, item[i]));
29944             }
29945             return buttons;
29946         }
29947         if (!(item instanceof Roo.Toolbar.Button)){
29948            item = new Roo.Toolbar.Button(item);
29949         }
29950         var td = document.createElement("td");
29951         this.tr.insertBefore(td, this.tr.childNodes[index]);
29952         item.render(td);
29953         this.items.insert(index, item);
29954         return item;
29955     },
29956     
29957     /**
29958      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29959      * @param {Object} config
29960      * @return {Roo.Toolbar.Item} The element's item
29961      */
29962     addDom : function(config, returnEl){
29963         var td = this.nextBlock();
29964         Roo.DomHelper.overwrite(td, config);
29965         var ti = new Roo.Toolbar.Item(td.firstChild);
29966         ti.render(td);
29967         this.items.add(ti);
29968         return ti;
29969     },
29970
29971     /**
29972      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29973      * @type Roo.util.MixedCollection  
29974      */
29975     fields : false,
29976     
29977     /**
29978      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29979      * Note: the field should not have been rendered yet. For a field that has already been
29980      * rendered, use {@link #addElement}.
29981      * @param {Roo.form.Field} field
29982      * @return {Roo.ToolbarItem}
29983      */
29984      
29985       
29986     addField : function(field) {
29987         if (!this.fields) {
29988             var autoId = 0;
29989             this.fields = new Roo.util.MixedCollection(false, function(o){
29990                 return o.id || ("item" + (++autoId));
29991             });
29992
29993         }
29994         
29995         var td = this.nextBlock();
29996         field.render(td);
29997         var ti = new Roo.Toolbar.Item(td.firstChild);
29998         ti.render(td);
29999         this.items.add(ti);
30000         this.fields.add(field);
30001         return ti;
30002     },
30003     /**
30004      * Hide the toolbar
30005      * @method hide
30006      */
30007      
30008       
30009     hide : function()
30010     {
30011         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30012         this.el.child('div').hide();
30013     },
30014     /**
30015      * Show the toolbar
30016      * @method show
30017      */
30018     show : function()
30019     {
30020         this.el.child('div').show();
30021     },
30022       
30023     // private
30024     nextBlock : function(){
30025         var td = document.createElement("td");
30026         this.tr.appendChild(td);
30027         return td;
30028     },
30029
30030     // private
30031     destroy : function(){
30032         if(this.items){ // rendered?
30033             Roo.destroy.apply(Roo, this.items.items);
30034         }
30035         if(this.fields){ // rendered?
30036             Roo.destroy.apply(Roo, this.fields.items);
30037         }
30038         Roo.Element.uncache(this.el, this.tr);
30039     }
30040 };
30041
30042 /**
30043  * @class Roo.Toolbar.Item
30044  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30045  * @constructor
30046  * Creates a new Item
30047  * @param {HTMLElement} el 
30048  */
30049 Roo.Toolbar.Item = function(el){
30050     var cfg = {};
30051     if (typeof (el.xtype) != 'undefined') {
30052         cfg = el;
30053         el = cfg.el;
30054     }
30055     
30056     this.el = Roo.getDom(el);
30057     this.id = Roo.id(this.el);
30058     this.hidden = false;
30059     
30060     this.addEvents({
30061          /**
30062              * @event render
30063              * Fires when the button is rendered
30064              * @param {Button} this
30065              */
30066         'render': true
30067     });
30068     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30069 };
30070 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30071 //Roo.Toolbar.Item.prototype = {
30072     
30073     /**
30074      * Get this item's HTML Element
30075      * @return {HTMLElement}
30076      */
30077     getEl : function(){
30078        return this.el;  
30079     },
30080
30081     // private
30082     render : function(td){
30083         
30084          this.td = td;
30085         td.appendChild(this.el);
30086         
30087         this.fireEvent('render', this);
30088     },
30089     
30090     /**
30091      * Removes and destroys this item.
30092      */
30093     destroy : function(){
30094         this.td.parentNode.removeChild(this.td);
30095     },
30096     
30097     /**
30098      * Shows this item.
30099      */
30100     show: function(){
30101         this.hidden = false;
30102         this.td.style.display = "";
30103     },
30104     
30105     /**
30106      * Hides this item.
30107      */
30108     hide: function(){
30109         this.hidden = true;
30110         this.td.style.display = "none";
30111     },
30112     
30113     /**
30114      * Convenience function for boolean show/hide.
30115      * @param {Boolean} visible true to show/false to hide
30116      */
30117     setVisible: function(visible){
30118         if(visible) {
30119             this.show();
30120         }else{
30121             this.hide();
30122         }
30123     },
30124     
30125     /**
30126      * Try to focus this item.
30127      */
30128     focus : function(){
30129         Roo.fly(this.el).focus();
30130     },
30131     
30132     /**
30133      * Disables this item.
30134      */
30135     disable : function(){
30136         Roo.fly(this.td).addClass("x-item-disabled");
30137         this.disabled = true;
30138         this.el.disabled = true;
30139     },
30140     
30141     /**
30142      * Enables this item.
30143      */
30144     enable : function(){
30145         Roo.fly(this.td).removeClass("x-item-disabled");
30146         this.disabled = false;
30147         this.el.disabled = false;
30148     }
30149 });
30150
30151
30152 /**
30153  * @class Roo.Toolbar.Separator
30154  * @extends Roo.Toolbar.Item
30155  * A simple toolbar separator class
30156  * @constructor
30157  * Creates a new Separator
30158  */
30159 Roo.Toolbar.Separator = function(cfg){
30160     
30161     var s = document.createElement("span");
30162     s.className = "ytb-sep";
30163     if (cfg) {
30164         cfg.el = s;
30165     }
30166     
30167     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30168 };
30169 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30170     enable:Roo.emptyFn,
30171     disable:Roo.emptyFn,
30172     focus:Roo.emptyFn
30173 });
30174
30175 /**
30176  * @class Roo.Toolbar.Spacer
30177  * @extends Roo.Toolbar.Item
30178  * A simple element that adds extra horizontal space to a toolbar.
30179  * @constructor
30180  * Creates a new Spacer
30181  */
30182 Roo.Toolbar.Spacer = function(cfg){
30183     var s = document.createElement("div");
30184     s.className = "ytb-spacer";
30185     if (cfg) {
30186         cfg.el = s;
30187     }
30188     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30189 };
30190 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30191     enable:Roo.emptyFn,
30192     disable:Roo.emptyFn,
30193     focus:Roo.emptyFn
30194 });
30195
30196 /**
30197  * @class Roo.Toolbar.Fill
30198  * @extends Roo.Toolbar.Spacer
30199  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30200  * @constructor
30201  * Creates a new Spacer
30202  */
30203 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30204     // private
30205     render : function(td){
30206         td.style.width = '100%';
30207         Roo.Toolbar.Fill.superclass.render.call(this, td);
30208     }
30209 });
30210
30211 /**
30212  * @class Roo.Toolbar.TextItem
30213  * @extends Roo.Toolbar.Item
30214  * A simple class that renders text directly into a toolbar.
30215  * @constructor
30216  * Creates a new TextItem
30217  * @param {String} text
30218  */
30219 Roo.Toolbar.TextItem = function(cfg){
30220     var  text = cfg || "";
30221     if (typeof(cfg) == 'object') {
30222         text = cfg.text || "";
30223     }  else {
30224         cfg = null;
30225     }
30226     var s = document.createElement("span");
30227     s.className = "ytb-text";
30228     s.innerHTML = text;
30229     if (cfg) {
30230         cfg.el  = s;
30231     }
30232     
30233     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30234 };
30235 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30236     
30237      
30238     enable:Roo.emptyFn,
30239     disable:Roo.emptyFn,
30240     focus:Roo.emptyFn
30241 });
30242
30243 /**
30244  * @class Roo.Toolbar.Button
30245  * @extends Roo.Button
30246  * A button that renders into a toolbar.
30247  * @constructor
30248  * Creates a new Button
30249  * @param {Object} config A standard {@link Roo.Button} config object
30250  */
30251 Roo.Toolbar.Button = function(config){
30252     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30253 };
30254 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30255     render : function(td){
30256         this.td = td;
30257         Roo.Toolbar.Button.superclass.render.call(this, td);
30258     },
30259     
30260     /**
30261      * Removes and destroys this button
30262      */
30263     destroy : function(){
30264         Roo.Toolbar.Button.superclass.destroy.call(this);
30265         this.td.parentNode.removeChild(this.td);
30266     },
30267     
30268     /**
30269      * Shows this button
30270      */
30271     show: function(){
30272         this.hidden = false;
30273         this.td.style.display = "";
30274     },
30275     
30276     /**
30277      * Hides this button
30278      */
30279     hide: function(){
30280         this.hidden = true;
30281         this.td.style.display = "none";
30282     },
30283
30284     /**
30285      * Disables this item
30286      */
30287     disable : function(){
30288         Roo.fly(this.td).addClass("x-item-disabled");
30289         this.disabled = true;
30290     },
30291
30292     /**
30293      * Enables this item
30294      */
30295     enable : function(){
30296         Roo.fly(this.td).removeClass("x-item-disabled");
30297         this.disabled = false;
30298     }
30299 });
30300 // backwards compat
30301 Roo.ToolbarButton = Roo.Toolbar.Button;
30302
30303 /**
30304  * @class Roo.Toolbar.SplitButton
30305  * @extends Roo.SplitButton
30306  * A menu button that renders into a toolbar.
30307  * @constructor
30308  * Creates a new SplitButton
30309  * @param {Object} config A standard {@link Roo.SplitButton} config object
30310  */
30311 Roo.Toolbar.SplitButton = function(config){
30312     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30313 };
30314 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30315     render : function(td){
30316         this.td = td;
30317         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30318     },
30319     
30320     /**
30321      * Removes and destroys this button
30322      */
30323     destroy : function(){
30324         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30325         this.td.parentNode.removeChild(this.td);
30326     },
30327     
30328     /**
30329      * Shows this button
30330      */
30331     show: function(){
30332         this.hidden = false;
30333         this.td.style.display = "";
30334     },
30335     
30336     /**
30337      * Hides this button
30338      */
30339     hide: function(){
30340         this.hidden = true;
30341         this.td.style.display = "none";
30342     }
30343 });
30344
30345 // backwards compat
30346 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30347  * Based on:
30348  * Ext JS Library 1.1.1
30349  * Copyright(c) 2006-2007, Ext JS, LLC.
30350  *
30351  * Originally Released Under LGPL - original licence link has changed is not relivant.
30352  *
30353  * Fork - LGPL
30354  * <script type="text/javascript">
30355  */
30356  
30357 /**
30358  * @class Roo.PagingToolbar
30359  * @extends Roo.Toolbar
30360  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30361  * @constructor
30362  * Create a new PagingToolbar
30363  * @param {Object} config The config object
30364  */
30365 Roo.PagingToolbar = function(el, ds, config)
30366 {
30367     // old args format still supported... - xtype is prefered..
30368     if (typeof(el) == 'object' && el.xtype) {
30369         // created from xtype...
30370         config = el;
30371         ds = el.dataSource;
30372         el = config.container;
30373     }
30374     var items = [];
30375     if (config.items) {
30376         items = config.items;
30377         config.items = [];
30378     }
30379     
30380     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30381     this.ds = ds;
30382     this.cursor = 0;
30383     this.renderButtons(this.el);
30384     this.bind(ds);
30385     
30386     // supprot items array.
30387    
30388     Roo.each(items, function(e) {
30389         this.add(Roo.factory(e));
30390     },this);
30391     
30392 };
30393
30394 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30395     /**
30396      * @cfg {Roo.data.Store} dataSource
30397      * The underlying data store providing the paged data
30398      */
30399     /**
30400      * @cfg {String/HTMLElement/Element} container
30401      * container The id or element that will contain the toolbar
30402      */
30403     /**
30404      * @cfg {Boolean} displayInfo
30405      * True to display the displayMsg (defaults to false)
30406      */
30407     /**
30408      * @cfg {Number} pageSize
30409      * The number of records to display per page (defaults to 20)
30410      */
30411     pageSize: 20,
30412     /**
30413      * @cfg {String} displayMsg
30414      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30415      */
30416     displayMsg : 'Displaying {0} - {1} of {2}',
30417     /**
30418      * @cfg {String} emptyMsg
30419      * The message to display when no records are found (defaults to "No data to display")
30420      */
30421     emptyMsg : 'No data to display',
30422     /**
30423      * Customizable piece of the default paging text (defaults to "Page")
30424      * @type String
30425      */
30426     beforePageText : "Page",
30427     /**
30428      * Customizable piece of the default paging text (defaults to "of %0")
30429      * @type String
30430      */
30431     afterPageText : "of {0}",
30432     /**
30433      * Customizable piece of the default paging text (defaults to "First Page")
30434      * @type String
30435      */
30436     firstText : "First Page",
30437     /**
30438      * Customizable piece of the default paging text (defaults to "Previous Page")
30439      * @type String
30440      */
30441     prevText : "Previous Page",
30442     /**
30443      * Customizable piece of the default paging text (defaults to "Next Page")
30444      * @type String
30445      */
30446     nextText : "Next Page",
30447     /**
30448      * Customizable piece of the default paging text (defaults to "Last Page")
30449      * @type String
30450      */
30451     lastText : "Last Page",
30452     /**
30453      * Customizable piece of the default paging text (defaults to "Refresh")
30454      * @type String
30455      */
30456     refreshText : "Refresh",
30457
30458     // private
30459     renderButtons : function(el){
30460         Roo.PagingToolbar.superclass.render.call(this, el);
30461         this.first = this.addButton({
30462             tooltip: this.firstText,
30463             cls: "x-btn-icon x-grid-page-first",
30464             disabled: true,
30465             handler: this.onClick.createDelegate(this, ["first"])
30466         });
30467         this.prev = this.addButton({
30468             tooltip: this.prevText,
30469             cls: "x-btn-icon x-grid-page-prev",
30470             disabled: true,
30471             handler: this.onClick.createDelegate(this, ["prev"])
30472         });
30473         //this.addSeparator();
30474         this.add(this.beforePageText);
30475         this.field = Roo.get(this.addDom({
30476            tag: "input",
30477            type: "text",
30478            size: "3",
30479            value: "1",
30480            cls: "x-grid-page-number"
30481         }).el);
30482         this.field.on("keydown", this.onPagingKeydown, this);
30483         this.field.on("focus", function(){this.dom.select();});
30484         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30485         this.field.setHeight(18);
30486         //this.addSeparator();
30487         this.next = this.addButton({
30488             tooltip: this.nextText,
30489             cls: "x-btn-icon x-grid-page-next",
30490             disabled: true,
30491             handler: this.onClick.createDelegate(this, ["next"])
30492         });
30493         this.last = this.addButton({
30494             tooltip: this.lastText,
30495             cls: "x-btn-icon x-grid-page-last",
30496             disabled: true,
30497             handler: this.onClick.createDelegate(this, ["last"])
30498         });
30499         //this.addSeparator();
30500         this.loading = this.addButton({
30501             tooltip: this.refreshText,
30502             cls: "x-btn-icon x-grid-loading",
30503             handler: this.onClick.createDelegate(this, ["refresh"])
30504         });
30505
30506         if(this.displayInfo){
30507             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30508         }
30509     },
30510
30511     // private
30512     updateInfo : function(){
30513         if(this.displayEl){
30514             var count = this.ds.getCount();
30515             var msg = count == 0 ?
30516                 this.emptyMsg :
30517                 String.format(
30518                     this.displayMsg,
30519                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30520                 );
30521             this.displayEl.update(msg);
30522         }
30523     },
30524
30525     // private
30526     onLoad : function(ds, r, o){
30527        this.cursor = o.params ? o.params.start : 0;
30528        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30529
30530        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30531        this.field.dom.value = ap;
30532        this.first.setDisabled(ap == 1);
30533        this.prev.setDisabled(ap == 1);
30534        this.next.setDisabled(ap == ps);
30535        this.last.setDisabled(ap == ps);
30536        this.loading.enable();
30537        this.updateInfo();
30538     },
30539
30540     // private
30541     getPageData : function(){
30542         var total = this.ds.getTotalCount();
30543         return {
30544             total : total,
30545             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30546             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30547         };
30548     },
30549
30550     // private
30551     onLoadError : function(){
30552         this.loading.enable();
30553     },
30554
30555     // private
30556     onPagingKeydown : function(e){
30557         var k = e.getKey();
30558         var d = this.getPageData();
30559         if(k == e.RETURN){
30560             var v = this.field.dom.value, pageNum;
30561             if(!v || isNaN(pageNum = parseInt(v, 10))){
30562                 this.field.dom.value = d.activePage;
30563                 return;
30564             }
30565             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30566             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30567             e.stopEvent();
30568         }
30569         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))
30570         {
30571           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30572           this.field.dom.value = pageNum;
30573           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30574           e.stopEvent();
30575         }
30576         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30577         {
30578           var v = this.field.dom.value, pageNum; 
30579           var increment = (e.shiftKey) ? 10 : 1;
30580           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30581             increment *= -1;
30582           }
30583           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30584             this.field.dom.value = d.activePage;
30585             return;
30586           }
30587           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30588           {
30589             this.field.dom.value = parseInt(v, 10) + increment;
30590             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30591             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30592           }
30593           e.stopEvent();
30594         }
30595     },
30596
30597     // private
30598     beforeLoad : function(){
30599         if(this.loading){
30600             this.loading.disable();
30601         }
30602     },
30603
30604     // private
30605     onClick : function(which){
30606         var ds = this.ds;
30607         switch(which){
30608             case "first":
30609                 ds.load({params:{start: 0, limit: this.pageSize}});
30610             break;
30611             case "prev":
30612                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30613             break;
30614             case "next":
30615                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30616             break;
30617             case "last":
30618                 var total = ds.getTotalCount();
30619                 var extra = total % this.pageSize;
30620                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30621                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30622             break;
30623             case "refresh":
30624                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30625             break;
30626         }
30627     },
30628
30629     /**
30630      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30631      * @param {Roo.data.Store} store The data store to unbind
30632      */
30633     unbind : function(ds){
30634         ds.un("beforeload", this.beforeLoad, this);
30635         ds.un("load", this.onLoad, this);
30636         ds.un("loadexception", this.onLoadError, this);
30637         ds.un("remove", this.updateInfo, this);
30638         ds.un("add", this.updateInfo, this);
30639         this.ds = undefined;
30640     },
30641
30642     /**
30643      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30644      * @param {Roo.data.Store} store The data store to bind
30645      */
30646     bind : function(ds){
30647         ds.on("beforeload", this.beforeLoad, this);
30648         ds.on("load", this.onLoad, this);
30649         ds.on("loadexception", this.onLoadError, this);
30650         ds.on("remove", this.updateInfo, this);
30651         ds.on("add", this.updateInfo, this);
30652         this.ds = ds;
30653     }
30654 });/*
30655  * Based on:
30656  * Ext JS Library 1.1.1
30657  * Copyright(c) 2006-2007, Ext JS, LLC.
30658  *
30659  * Originally Released Under LGPL - original licence link has changed is not relivant.
30660  *
30661  * Fork - LGPL
30662  * <script type="text/javascript">
30663  */
30664
30665 /**
30666  * @class Roo.Resizable
30667  * @extends Roo.util.Observable
30668  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30669  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30670  * 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
30671  * the element will be wrapped for you automatically.</p>
30672  * <p>Here is the list of valid resize handles:</p>
30673  * <pre>
30674 Value   Description
30675 ------  -------------------
30676  'n'     north
30677  's'     south
30678  'e'     east
30679  'w'     west
30680  'nw'    northwest
30681  'sw'    southwest
30682  'se'    southeast
30683  'ne'    northeast
30684  'hd'    horizontal drag
30685  'all'   all
30686 </pre>
30687  * <p>Here's an example showing the creation of a typical Resizable:</p>
30688  * <pre><code>
30689 var resizer = new Roo.Resizable("element-id", {
30690     handles: 'all',
30691     minWidth: 200,
30692     minHeight: 100,
30693     maxWidth: 500,
30694     maxHeight: 400,
30695     pinned: true
30696 });
30697 resizer.on("resize", myHandler);
30698 </code></pre>
30699  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30700  * resizer.east.setDisplayed(false);</p>
30701  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30702  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30703  * resize operation's new size (defaults to [0, 0])
30704  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30705  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30706  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30707  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30708  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30709  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30710  * @cfg {Number} width The width of the element in pixels (defaults to null)
30711  * @cfg {Number} height The height of the element in pixels (defaults to null)
30712  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30713  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30714  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30715  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30716  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30717  * in favor of the handles config option (defaults to false)
30718  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30719  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30720  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30721  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30722  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30723  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30724  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30725  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30726  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30727  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30728  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30729  * @constructor
30730  * Create a new resizable component
30731  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30732  * @param {Object} config configuration options
30733   */
30734 Roo.Resizable = function(el, config)
30735 {
30736     this.el = Roo.get(el);
30737
30738     if(config && config.wrap){
30739         config.resizeChild = this.el;
30740         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30741         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30742         this.el.setStyle("overflow", "hidden");
30743         this.el.setPositioning(config.resizeChild.getPositioning());
30744         config.resizeChild.clearPositioning();
30745         if(!config.width || !config.height){
30746             var csize = config.resizeChild.getSize();
30747             this.el.setSize(csize.width, csize.height);
30748         }
30749         if(config.pinned && !config.adjustments){
30750             config.adjustments = "auto";
30751         }
30752     }
30753
30754     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30755     this.proxy.unselectable();
30756     this.proxy.enableDisplayMode('block');
30757
30758     Roo.apply(this, config);
30759
30760     if(this.pinned){
30761         this.disableTrackOver = true;
30762         this.el.addClass("x-resizable-pinned");
30763     }
30764     // if the element isn't positioned, make it relative
30765     var position = this.el.getStyle("position");
30766     if(position != "absolute" && position != "fixed"){
30767         this.el.setStyle("position", "relative");
30768     }
30769     if(!this.handles){ // no handles passed, must be legacy style
30770         this.handles = 's,e,se';
30771         if(this.multiDirectional){
30772             this.handles += ',n,w';
30773         }
30774     }
30775     if(this.handles == "all"){
30776         this.handles = "n s e w ne nw se sw";
30777     }
30778     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30779     var ps = Roo.Resizable.positions;
30780     for(var i = 0, len = hs.length; i < len; i++){
30781         if(hs[i] && ps[hs[i]]){
30782             var pos = ps[hs[i]];
30783             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30784         }
30785     }
30786     // legacy
30787     this.corner = this.southeast;
30788     
30789     // updateBox = the box can move..
30790     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30791         this.updateBox = true;
30792     }
30793
30794     this.activeHandle = null;
30795
30796     if(this.resizeChild){
30797         if(typeof this.resizeChild == "boolean"){
30798             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30799         }else{
30800             this.resizeChild = Roo.get(this.resizeChild, true);
30801         }
30802     }
30803     
30804     if(this.adjustments == "auto"){
30805         var rc = this.resizeChild;
30806         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30807         if(rc && (hw || hn)){
30808             rc.position("relative");
30809             rc.setLeft(hw ? hw.el.getWidth() : 0);
30810             rc.setTop(hn ? hn.el.getHeight() : 0);
30811         }
30812         this.adjustments = [
30813             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30814             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30815         ];
30816     }
30817
30818     if(this.draggable){
30819         this.dd = this.dynamic ?
30820             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30821         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30822     }
30823
30824     // public events
30825     this.addEvents({
30826         /**
30827          * @event beforeresize
30828          * Fired before resize is allowed. Set enabled to false to cancel resize.
30829          * @param {Roo.Resizable} this
30830          * @param {Roo.EventObject} e The mousedown event
30831          */
30832         "beforeresize" : true,
30833         /**
30834          * @event resizing
30835          * Fired a resizing.
30836          * @param {Roo.Resizable} this
30837          * @param {Number} x The new x position
30838          * @param {Number} y The new y position
30839          * @param {Number} w The new w width
30840          * @param {Number} h The new h hight
30841          * @param {Roo.EventObject} e The mouseup event
30842          */
30843         "resizing" : true,
30844         /**
30845          * @event resize
30846          * Fired after a resize.
30847          * @param {Roo.Resizable} this
30848          * @param {Number} width The new width
30849          * @param {Number} height The new height
30850          * @param {Roo.EventObject} e The mouseup event
30851          */
30852         "resize" : true
30853     });
30854
30855     if(this.width !== null && this.height !== null){
30856         this.resizeTo(this.width, this.height);
30857     }else{
30858         this.updateChildSize();
30859     }
30860     if(Roo.isIE){
30861         this.el.dom.style.zoom = 1;
30862     }
30863     Roo.Resizable.superclass.constructor.call(this);
30864 };
30865
30866 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30867         resizeChild : false,
30868         adjustments : [0, 0],
30869         minWidth : 5,
30870         minHeight : 5,
30871         maxWidth : 10000,
30872         maxHeight : 10000,
30873         enabled : true,
30874         animate : false,
30875         duration : .35,
30876         dynamic : false,
30877         handles : false,
30878         multiDirectional : false,
30879         disableTrackOver : false,
30880         easing : 'easeOutStrong',
30881         widthIncrement : 0,
30882         heightIncrement : 0,
30883         pinned : false,
30884         width : null,
30885         height : null,
30886         preserveRatio : false,
30887         transparent: false,
30888         minX: 0,
30889         minY: 0,
30890         draggable: false,
30891
30892         /**
30893          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30894          */
30895         constrainTo: undefined,
30896         /**
30897          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30898          */
30899         resizeRegion: undefined,
30900
30901
30902     /**
30903      * Perform a manual resize
30904      * @param {Number} width
30905      * @param {Number} height
30906      */
30907     resizeTo : function(width, height){
30908         this.el.setSize(width, height);
30909         this.updateChildSize();
30910         this.fireEvent("resize", this, width, height, null);
30911     },
30912
30913     // private
30914     startSizing : function(e, handle){
30915         this.fireEvent("beforeresize", this, e);
30916         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30917
30918             if(!this.overlay){
30919                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30920                 this.overlay.unselectable();
30921                 this.overlay.enableDisplayMode("block");
30922                 this.overlay.on("mousemove", this.onMouseMove, this);
30923                 this.overlay.on("mouseup", this.onMouseUp, this);
30924             }
30925             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30926
30927             this.resizing = true;
30928             this.startBox = this.el.getBox();
30929             this.startPoint = e.getXY();
30930             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30931                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30932
30933             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30934             this.overlay.show();
30935
30936             if(this.constrainTo) {
30937                 var ct = Roo.get(this.constrainTo);
30938                 this.resizeRegion = ct.getRegion().adjust(
30939                     ct.getFrameWidth('t'),
30940                     ct.getFrameWidth('l'),
30941                     -ct.getFrameWidth('b'),
30942                     -ct.getFrameWidth('r')
30943                 );
30944             }
30945
30946             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30947             this.proxy.show();
30948             this.proxy.setBox(this.startBox);
30949             if(!this.dynamic){
30950                 this.proxy.setStyle('visibility', 'visible');
30951             }
30952         }
30953     },
30954
30955     // private
30956     onMouseDown : function(handle, e){
30957         if(this.enabled){
30958             e.stopEvent();
30959             this.activeHandle = handle;
30960             this.startSizing(e, handle);
30961         }
30962     },
30963
30964     // private
30965     onMouseUp : function(e){
30966         var size = this.resizeElement();
30967         this.resizing = false;
30968         this.handleOut();
30969         this.overlay.hide();
30970         this.proxy.hide();
30971         this.fireEvent("resize", this, size.width, size.height, e);
30972     },
30973
30974     // private
30975     updateChildSize : function(){
30976         
30977         if(this.resizeChild){
30978             var el = this.el;
30979             var child = this.resizeChild;
30980             var adj = this.adjustments;
30981             if(el.dom.offsetWidth){
30982                 var b = el.getSize(true);
30983                 child.setSize(b.width+adj[0], b.height+adj[1]);
30984             }
30985             // Second call here for IE
30986             // The first call enables instant resizing and
30987             // the second call corrects scroll bars if they
30988             // exist
30989             if(Roo.isIE){
30990                 setTimeout(function(){
30991                     if(el.dom.offsetWidth){
30992                         var b = el.getSize(true);
30993                         child.setSize(b.width+adj[0], b.height+adj[1]);
30994                     }
30995                 }, 10);
30996             }
30997         }
30998     },
30999
31000     // private
31001     snap : function(value, inc, min){
31002         if(!inc || !value) {
31003             return value;
31004         }
31005         var newValue = value;
31006         var m = value % inc;
31007         if(m > 0){
31008             if(m > (inc/2)){
31009                 newValue = value + (inc-m);
31010             }else{
31011                 newValue = value - m;
31012             }
31013         }
31014         return Math.max(min, newValue);
31015     },
31016
31017     // private
31018     resizeElement : function(){
31019         var box = this.proxy.getBox();
31020         if(this.updateBox){
31021             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31022         }else{
31023             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31024         }
31025         this.updateChildSize();
31026         if(!this.dynamic){
31027             this.proxy.hide();
31028         }
31029         return box;
31030     },
31031
31032     // private
31033     constrain : function(v, diff, m, mx){
31034         if(v - diff < m){
31035             diff = v - m;
31036         }else if(v - diff > mx){
31037             diff = mx - v;
31038         }
31039         return diff;
31040     },
31041
31042     // private
31043     onMouseMove : function(e){
31044         
31045         if(this.enabled){
31046             try{// try catch so if something goes wrong the user doesn't get hung
31047
31048             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31049                 return;
31050             }
31051
31052             //var curXY = this.startPoint;
31053             var curSize = this.curSize || this.startBox;
31054             var x = this.startBox.x, y = this.startBox.y;
31055             var ox = x, oy = y;
31056             var w = curSize.width, h = curSize.height;
31057             var ow = w, oh = h;
31058             var mw = this.minWidth, mh = this.minHeight;
31059             var mxw = this.maxWidth, mxh = this.maxHeight;
31060             var wi = this.widthIncrement;
31061             var hi = this.heightIncrement;
31062
31063             var eventXY = e.getXY();
31064             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31065             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31066
31067             var pos = this.activeHandle.position;
31068
31069             switch(pos){
31070                 case "east":
31071                     w += diffX;
31072                     w = Math.min(Math.max(mw, w), mxw);
31073                     break;
31074              
31075                 case "south":
31076                     h += diffY;
31077                     h = Math.min(Math.max(mh, h), mxh);
31078                     break;
31079                 case "southeast":
31080                     w += diffX;
31081                     h += diffY;
31082                     w = Math.min(Math.max(mw, w), mxw);
31083                     h = Math.min(Math.max(mh, h), mxh);
31084                     break;
31085                 case "north":
31086                     diffY = this.constrain(h, diffY, mh, mxh);
31087                     y += diffY;
31088                     h -= diffY;
31089                     break;
31090                 case "hdrag":
31091                     
31092                     if (wi) {
31093                         var adiffX = Math.abs(diffX);
31094                         var sub = (adiffX % wi); // how much 
31095                         if (sub > (wi/2)) { // far enough to snap
31096                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31097                         } else {
31098                             // remove difference.. 
31099                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31100                         }
31101                     }
31102                     x += diffX;
31103                     x = Math.max(this.minX, x);
31104                     break;
31105                 case "west":
31106                     diffX = this.constrain(w, diffX, mw, mxw);
31107                     x += diffX;
31108                     w -= diffX;
31109                     break;
31110                 case "northeast":
31111                     w += diffX;
31112                     w = Math.min(Math.max(mw, w), mxw);
31113                     diffY = this.constrain(h, diffY, mh, mxh);
31114                     y += diffY;
31115                     h -= diffY;
31116                     break;
31117                 case "northwest":
31118                     diffX = this.constrain(w, diffX, mw, mxw);
31119                     diffY = this.constrain(h, diffY, mh, mxh);
31120                     y += diffY;
31121                     h -= diffY;
31122                     x += diffX;
31123                     w -= diffX;
31124                     break;
31125                case "southwest":
31126                     diffX = this.constrain(w, diffX, mw, mxw);
31127                     h += diffY;
31128                     h = Math.min(Math.max(mh, h), mxh);
31129                     x += diffX;
31130                     w -= diffX;
31131                     break;
31132             }
31133
31134             var sw = this.snap(w, wi, mw);
31135             var sh = this.snap(h, hi, mh);
31136             if(sw != w || sh != h){
31137                 switch(pos){
31138                     case "northeast":
31139                         y -= sh - h;
31140                     break;
31141                     case "north":
31142                         y -= sh - h;
31143                         break;
31144                     case "southwest":
31145                         x -= sw - w;
31146                     break;
31147                     case "west":
31148                         x -= sw - w;
31149                         break;
31150                     case "northwest":
31151                         x -= sw - w;
31152                         y -= sh - h;
31153                     break;
31154                 }
31155                 w = sw;
31156                 h = sh;
31157             }
31158
31159             if(this.preserveRatio){
31160                 switch(pos){
31161                     case "southeast":
31162                     case "east":
31163                         h = oh * (w/ow);
31164                         h = Math.min(Math.max(mh, h), mxh);
31165                         w = ow * (h/oh);
31166                        break;
31167                     case "south":
31168                         w = ow * (h/oh);
31169                         w = Math.min(Math.max(mw, w), mxw);
31170                         h = oh * (w/ow);
31171                         break;
31172                     case "northeast":
31173                         w = ow * (h/oh);
31174                         w = Math.min(Math.max(mw, w), mxw);
31175                         h = oh * (w/ow);
31176                     break;
31177                     case "north":
31178                         var tw = w;
31179                         w = ow * (h/oh);
31180                         w = Math.min(Math.max(mw, w), mxw);
31181                         h = oh * (w/ow);
31182                         x += (tw - w) / 2;
31183                         break;
31184                     case "southwest":
31185                         h = oh * (w/ow);
31186                         h = Math.min(Math.max(mh, h), mxh);
31187                         var tw = w;
31188                         w = ow * (h/oh);
31189                         x += tw - w;
31190                         break;
31191                     case "west":
31192                         var th = h;
31193                         h = oh * (w/ow);
31194                         h = Math.min(Math.max(mh, h), mxh);
31195                         y += (th - h) / 2;
31196                         var tw = w;
31197                         w = ow * (h/oh);
31198                         x += tw - w;
31199                        break;
31200                     case "northwest":
31201                         var tw = w;
31202                         var th = h;
31203                         h = oh * (w/ow);
31204                         h = Math.min(Math.max(mh, h), mxh);
31205                         w = ow * (h/oh);
31206                         y += th - h;
31207                         x += tw - w;
31208                        break;
31209
31210                 }
31211             }
31212             if (pos == 'hdrag') {
31213                 w = ow;
31214             }
31215             this.proxy.setBounds(x, y, w, h);
31216             if(this.dynamic){
31217                 this.resizeElement();
31218             }
31219             }catch(e){}
31220         }
31221         this.fireEvent("resizing", this, x, y, w, h, e);
31222     },
31223
31224     // private
31225     handleOver : function(){
31226         if(this.enabled){
31227             this.el.addClass("x-resizable-over");
31228         }
31229     },
31230
31231     // private
31232     handleOut : function(){
31233         if(!this.resizing){
31234             this.el.removeClass("x-resizable-over");
31235         }
31236     },
31237
31238     /**
31239      * Returns the element this component is bound to.
31240      * @return {Roo.Element}
31241      */
31242     getEl : function(){
31243         return this.el;
31244     },
31245
31246     /**
31247      * Returns the resizeChild element (or null).
31248      * @return {Roo.Element}
31249      */
31250     getResizeChild : function(){
31251         return this.resizeChild;
31252     },
31253     groupHandler : function()
31254     {
31255         
31256     },
31257     /**
31258      * Destroys this resizable. If the element was wrapped and
31259      * removeEl is not true then the element remains.
31260      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31261      */
31262     destroy : function(removeEl){
31263         this.proxy.remove();
31264         if(this.overlay){
31265             this.overlay.removeAllListeners();
31266             this.overlay.remove();
31267         }
31268         var ps = Roo.Resizable.positions;
31269         for(var k in ps){
31270             if(typeof ps[k] != "function" && this[ps[k]]){
31271                 var h = this[ps[k]];
31272                 h.el.removeAllListeners();
31273                 h.el.remove();
31274             }
31275         }
31276         if(removeEl){
31277             this.el.update("");
31278             this.el.remove();
31279         }
31280     }
31281 });
31282
31283 // private
31284 // hash to map config positions to true positions
31285 Roo.Resizable.positions = {
31286     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31287     hd: "hdrag"
31288 };
31289
31290 // private
31291 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31292     if(!this.tpl){
31293         // only initialize the template if resizable is used
31294         var tpl = Roo.DomHelper.createTemplate(
31295             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31296         );
31297         tpl.compile();
31298         Roo.Resizable.Handle.prototype.tpl = tpl;
31299     }
31300     this.position = pos;
31301     this.rz = rz;
31302     // show north drag fro topdra
31303     var handlepos = pos == 'hdrag' ? 'north' : pos;
31304     
31305     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31306     if (pos == 'hdrag') {
31307         this.el.setStyle('cursor', 'pointer');
31308     }
31309     this.el.unselectable();
31310     if(transparent){
31311         this.el.setOpacity(0);
31312     }
31313     this.el.on("mousedown", this.onMouseDown, this);
31314     if(!disableTrackOver){
31315         this.el.on("mouseover", this.onMouseOver, this);
31316         this.el.on("mouseout", this.onMouseOut, this);
31317     }
31318 };
31319
31320 // private
31321 Roo.Resizable.Handle.prototype = {
31322     afterResize : function(rz){
31323         Roo.log('after?');
31324         // do nothing
31325     },
31326     // private
31327     onMouseDown : function(e){
31328         this.rz.onMouseDown(this, e);
31329     },
31330     // private
31331     onMouseOver : function(e){
31332         this.rz.handleOver(this, e);
31333     },
31334     // private
31335     onMouseOut : function(e){
31336         this.rz.handleOut(this, e);
31337     }
31338 };/*
31339  * Based on:
31340  * Ext JS Library 1.1.1
31341  * Copyright(c) 2006-2007, Ext JS, LLC.
31342  *
31343  * Originally Released Under LGPL - original licence link has changed is not relivant.
31344  *
31345  * Fork - LGPL
31346  * <script type="text/javascript">
31347  */
31348
31349 /**
31350  * @class Roo.Editor
31351  * @extends Roo.Component
31352  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31353  * @constructor
31354  * Create a new Editor
31355  * @param {Roo.form.Field} field The Field object (or descendant)
31356  * @param {Object} config The config object
31357  */
31358 Roo.Editor = function(field, config){
31359     Roo.Editor.superclass.constructor.call(this, config);
31360     this.field = field;
31361     this.addEvents({
31362         /**
31363              * @event beforestartedit
31364              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31365              * false from the handler of this event.
31366              * @param {Editor} this
31367              * @param {Roo.Element} boundEl The underlying element bound to this editor
31368              * @param {Mixed} value The field value being set
31369              */
31370         "beforestartedit" : true,
31371         /**
31372              * @event startedit
31373              * Fires when this editor is displayed
31374              * @param {Roo.Element} boundEl The underlying element bound to this editor
31375              * @param {Mixed} value The starting field value
31376              */
31377         "startedit" : true,
31378         /**
31379              * @event beforecomplete
31380              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31381              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31382              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31383              * event will not fire since no edit actually occurred.
31384              * @param {Editor} this
31385              * @param {Mixed} value The current field value
31386              * @param {Mixed} startValue The original field value
31387              */
31388         "beforecomplete" : true,
31389         /**
31390              * @event complete
31391              * Fires after editing is complete and any changed value has been written to the underlying field.
31392              * @param {Editor} this
31393              * @param {Mixed} value The current field value
31394              * @param {Mixed} startValue The original field value
31395              */
31396         "complete" : true,
31397         /**
31398          * @event specialkey
31399          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31400          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31401          * @param {Roo.form.Field} this
31402          * @param {Roo.EventObject} e The event object
31403          */
31404         "specialkey" : true
31405     });
31406 };
31407
31408 Roo.extend(Roo.Editor, Roo.Component, {
31409     /**
31410      * @cfg {Boolean/String} autosize
31411      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31412      * or "height" to adopt the height only (defaults to false)
31413      */
31414     /**
31415      * @cfg {Boolean} revertInvalid
31416      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31417      * validation fails (defaults to true)
31418      */
31419     /**
31420      * @cfg {Boolean} ignoreNoChange
31421      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31422      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31423      * will never be ignored.
31424      */
31425     /**
31426      * @cfg {Boolean} hideEl
31427      * False to keep the bound element visible while the editor is displayed (defaults to true)
31428      */
31429     /**
31430      * @cfg {Mixed} value
31431      * The data value of the underlying field (defaults to "")
31432      */
31433     value : "",
31434     /**
31435      * @cfg {String} alignment
31436      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31437      */
31438     alignment: "c-c?",
31439     /**
31440      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31441      * for bottom-right shadow (defaults to "frame")
31442      */
31443     shadow : "frame",
31444     /**
31445      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31446      */
31447     constrain : false,
31448     /**
31449      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31450      */
31451     completeOnEnter : false,
31452     /**
31453      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31454      */
31455     cancelOnEsc : false,
31456     /**
31457      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31458      */
31459     updateEl : false,
31460
31461     // private
31462     onRender : function(ct, position){
31463         this.el = new Roo.Layer({
31464             shadow: this.shadow,
31465             cls: "x-editor",
31466             parentEl : ct,
31467             shim : this.shim,
31468             shadowOffset:4,
31469             id: this.id,
31470             constrain: this.constrain
31471         });
31472         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31473         if(this.field.msgTarget != 'title'){
31474             this.field.msgTarget = 'qtip';
31475         }
31476         this.field.render(this.el);
31477         if(Roo.isGecko){
31478             this.field.el.dom.setAttribute('autocomplete', 'off');
31479         }
31480         this.field.on("specialkey", this.onSpecialKey, this);
31481         if(this.swallowKeys){
31482             this.field.el.swallowEvent(['keydown','keypress']);
31483         }
31484         this.field.show();
31485         this.field.on("blur", this.onBlur, this);
31486         if(this.field.grow){
31487             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31488         }
31489     },
31490
31491     onSpecialKey : function(field, e)
31492     {
31493         //Roo.log('editor onSpecialKey');
31494         if(this.completeOnEnter && e.getKey() == e.ENTER){
31495             e.stopEvent();
31496             this.completeEdit();
31497             return;
31498         }
31499         // do not fire special key otherwise it might hide close the editor...
31500         if(e.getKey() == e.ENTER){    
31501             return;
31502         }
31503         if(this.cancelOnEsc && e.getKey() == e.ESC){
31504             this.cancelEdit();
31505             return;
31506         } 
31507         this.fireEvent('specialkey', field, e);
31508     
31509     },
31510
31511     /**
31512      * Starts the editing process and shows the editor.
31513      * @param {String/HTMLElement/Element} el The element to edit
31514      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31515       * to the innerHTML of el.
31516      */
31517     startEdit : function(el, value){
31518         if(this.editing){
31519             this.completeEdit();
31520         }
31521         this.boundEl = Roo.get(el);
31522         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31523         if(!this.rendered){
31524             this.render(this.parentEl || document.body);
31525         }
31526         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31527             return;
31528         }
31529         this.startValue = v;
31530         this.field.setValue(v);
31531         if(this.autoSize){
31532             var sz = this.boundEl.getSize();
31533             switch(this.autoSize){
31534                 case "width":
31535                 this.setSize(sz.width,  "");
31536                 break;
31537                 case "height":
31538                 this.setSize("",  sz.height);
31539                 break;
31540                 default:
31541                 this.setSize(sz.width,  sz.height);
31542             }
31543         }
31544         this.el.alignTo(this.boundEl, this.alignment);
31545         this.editing = true;
31546         if(Roo.QuickTips){
31547             Roo.QuickTips.disable();
31548         }
31549         this.show();
31550     },
31551
31552     /**
31553      * Sets the height and width of this editor.
31554      * @param {Number} width The new width
31555      * @param {Number} height The new height
31556      */
31557     setSize : function(w, h){
31558         this.field.setSize(w, h);
31559         if(this.el){
31560             this.el.sync();
31561         }
31562     },
31563
31564     /**
31565      * Realigns the editor to the bound field based on the current alignment config value.
31566      */
31567     realign : function(){
31568         this.el.alignTo(this.boundEl, this.alignment);
31569     },
31570
31571     /**
31572      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31573      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31574      */
31575     completeEdit : function(remainVisible){
31576         if(!this.editing){
31577             return;
31578         }
31579         var v = this.getValue();
31580         if(this.revertInvalid !== false && !this.field.isValid()){
31581             v = this.startValue;
31582             this.cancelEdit(true);
31583         }
31584         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31585             this.editing = false;
31586             this.hide();
31587             return;
31588         }
31589         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31590             this.editing = false;
31591             if(this.updateEl && this.boundEl){
31592                 this.boundEl.update(v);
31593             }
31594             if(remainVisible !== true){
31595                 this.hide();
31596             }
31597             this.fireEvent("complete", this, v, this.startValue);
31598         }
31599     },
31600
31601     // private
31602     onShow : function(){
31603         this.el.show();
31604         if(this.hideEl !== false){
31605             this.boundEl.hide();
31606         }
31607         this.field.show();
31608         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31609             this.fixIEFocus = true;
31610             this.deferredFocus.defer(50, this);
31611         }else{
31612             this.field.focus();
31613         }
31614         this.fireEvent("startedit", this.boundEl, this.startValue);
31615     },
31616
31617     deferredFocus : function(){
31618         if(this.editing){
31619             this.field.focus();
31620         }
31621     },
31622
31623     /**
31624      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31625      * reverted to the original starting value.
31626      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31627      * cancel (defaults to false)
31628      */
31629     cancelEdit : function(remainVisible){
31630         if(this.editing){
31631             this.setValue(this.startValue);
31632             if(remainVisible !== true){
31633                 this.hide();
31634             }
31635         }
31636     },
31637
31638     // private
31639     onBlur : function(){
31640         if(this.allowBlur !== true && this.editing){
31641             this.completeEdit();
31642         }
31643     },
31644
31645     // private
31646     onHide : function(){
31647         if(this.editing){
31648             this.completeEdit();
31649             return;
31650         }
31651         this.field.blur();
31652         if(this.field.collapse){
31653             this.field.collapse();
31654         }
31655         this.el.hide();
31656         if(this.hideEl !== false){
31657             this.boundEl.show();
31658         }
31659         if(Roo.QuickTips){
31660             Roo.QuickTips.enable();
31661         }
31662     },
31663
31664     /**
31665      * Sets the data value of the editor
31666      * @param {Mixed} value Any valid value supported by the underlying field
31667      */
31668     setValue : function(v){
31669         this.field.setValue(v);
31670     },
31671
31672     /**
31673      * Gets the data value of the editor
31674      * @return {Mixed} The data value
31675      */
31676     getValue : function(){
31677         return this.field.getValue();
31678     }
31679 });/*
31680  * Based on:
31681  * Ext JS Library 1.1.1
31682  * Copyright(c) 2006-2007, Ext JS, LLC.
31683  *
31684  * Originally Released Under LGPL - original licence link has changed is not relivant.
31685  *
31686  * Fork - LGPL
31687  * <script type="text/javascript">
31688  */
31689  
31690 /**
31691  * @class Roo.BasicDialog
31692  * @extends Roo.util.Observable
31693  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31694  * <pre><code>
31695 var dlg = new Roo.BasicDialog("my-dlg", {
31696     height: 200,
31697     width: 300,
31698     minHeight: 100,
31699     minWidth: 150,
31700     modal: true,
31701     proxyDrag: true,
31702     shadow: true
31703 });
31704 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31705 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31706 dlg.addButton('Cancel', dlg.hide, dlg);
31707 dlg.show();
31708 </code></pre>
31709   <b>A Dialog should always be a direct child of the body element.</b>
31710  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31711  * @cfg {String} title Default text to display in the title bar (defaults to null)
31712  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31713  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31714  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31715  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31716  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31717  * (defaults to null with no animation)
31718  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31719  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31720  * property for valid values (defaults to 'all')
31721  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31722  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31723  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31724  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31725  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31726  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31727  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31728  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31729  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31730  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31731  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31732  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31733  * draggable = true (defaults to false)
31734  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31735  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31736  * shadow (defaults to false)
31737  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31738  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31739  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31740  * @cfg {Array} buttons Array of buttons
31741  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31742  * @constructor
31743  * Create a new BasicDialog.
31744  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31745  * @param {Object} config Configuration options
31746  */
31747 Roo.BasicDialog = function(el, config){
31748     this.el = Roo.get(el);
31749     var dh = Roo.DomHelper;
31750     if(!this.el && config && config.autoCreate){
31751         if(typeof config.autoCreate == "object"){
31752             if(!config.autoCreate.id){
31753                 config.autoCreate.id = el;
31754             }
31755             this.el = dh.append(document.body,
31756                         config.autoCreate, true);
31757         }else{
31758             this.el = dh.append(document.body,
31759                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31760         }
31761     }
31762     el = this.el;
31763     el.setDisplayed(true);
31764     el.hide = this.hideAction;
31765     this.id = el.id;
31766     el.addClass("x-dlg");
31767
31768     Roo.apply(this, config);
31769
31770     this.proxy = el.createProxy("x-dlg-proxy");
31771     this.proxy.hide = this.hideAction;
31772     this.proxy.setOpacity(.5);
31773     this.proxy.hide();
31774
31775     if(config.width){
31776         el.setWidth(config.width);
31777     }
31778     if(config.height){
31779         el.setHeight(config.height);
31780     }
31781     this.size = el.getSize();
31782     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31783         this.xy = [config.x,config.y];
31784     }else{
31785         this.xy = el.getCenterXY(true);
31786     }
31787     /** The header element @type Roo.Element */
31788     this.header = el.child("> .x-dlg-hd");
31789     /** The body element @type Roo.Element */
31790     this.body = el.child("> .x-dlg-bd");
31791     /** The footer element @type Roo.Element */
31792     this.footer = el.child("> .x-dlg-ft");
31793
31794     if(!this.header){
31795         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31796     }
31797     if(!this.body){
31798         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31799     }
31800
31801     this.header.unselectable();
31802     if(this.title){
31803         this.header.update(this.title);
31804     }
31805     // this element allows the dialog to be focused for keyboard event
31806     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31807     this.focusEl.swallowEvent("click", true);
31808
31809     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31810
31811     // wrap the body and footer for special rendering
31812     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31813     if(this.footer){
31814         this.bwrap.dom.appendChild(this.footer.dom);
31815     }
31816
31817     this.bg = this.el.createChild({
31818         tag: "div", cls:"x-dlg-bg",
31819         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31820     });
31821     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31822
31823
31824     if(this.autoScroll !== false && !this.autoTabs){
31825         this.body.setStyle("overflow", "auto");
31826     }
31827
31828     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31829
31830     if(this.closable !== false){
31831         this.el.addClass("x-dlg-closable");
31832         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31833         this.close.on("click", this.closeClick, this);
31834         this.close.addClassOnOver("x-dlg-close-over");
31835     }
31836     if(this.collapsible !== false){
31837         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31838         this.collapseBtn.on("click", this.collapseClick, this);
31839         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31840         this.header.on("dblclick", this.collapseClick, this);
31841     }
31842     if(this.resizable !== false){
31843         this.el.addClass("x-dlg-resizable");
31844         this.resizer = new Roo.Resizable(el, {
31845             minWidth: this.minWidth || 80,
31846             minHeight:this.minHeight || 80,
31847             handles: this.resizeHandles || "all",
31848             pinned: true
31849         });
31850         this.resizer.on("beforeresize", this.beforeResize, this);
31851         this.resizer.on("resize", this.onResize, this);
31852     }
31853     if(this.draggable !== false){
31854         el.addClass("x-dlg-draggable");
31855         if (!this.proxyDrag) {
31856             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31857         }
31858         else {
31859             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31860         }
31861         dd.setHandleElId(this.header.id);
31862         dd.endDrag = this.endMove.createDelegate(this);
31863         dd.startDrag = this.startMove.createDelegate(this);
31864         dd.onDrag = this.onDrag.createDelegate(this);
31865         dd.scroll = false;
31866         this.dd = dd;
31867     }
31868     if(this.modal){
31869         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31870         this.mask.enableDisplayMode("block");
31871         this.mask.hide();
31872         this.el.addClass("x-dlg-modal");
31873     }
31874     if(this.shadow){
31875         this.shadow = new Roo.Shadow({
31876             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31877             offset : this.shadowOffset
31878         });
31879     }else{
31880         this.shadowOffset = 0;
31881     }
31882     if(Roo.useShims && this.shim !== false){
31883         this.shim = this.el.createShim();
31884         this.shim.hide = this.hideAction;
31885         this.shim.hide();
31886     }else{
31887         this.shim = false;
31888     }
31889     if(this.autoTabs){
31890         this.initTabs();
31891     }
31892     if (this.buttons) { 
31893         var bts= this.buttons;
31894         this.buttons = [];
31895         Roo.each(bts, function(b) {
31896             this.addButton(b);
31897         }, this);
31898     }
31899     
31900     
31901     this.addEvents({
31902         /**
31903          * @event keydown
31904          * Fires when a key is pressed
31905          * @param {Roo.BasicDialog} this
31906          * @param {Roo.EventObject} e
31907          */
31908         "keydown" : true,
31909         /**
31910          * @event move
31911          * Fires when this dialog is moved by the user.
31912          * @param {Roo.BasicDialog} this
31913          * @param {Number} x The new page X
31914          * @param {Number} y The new page Y
31915          */
31916         "move" : true,
31917         /**
31918          * @event resize
31919          * Fires when this dialog is resized by the user.
31920          * @param {Roo.BasicDialog} this
31921          * @param {Number} width The new width
31922          * @param {Number} height The new height
31923          */
31924         "resize" : true,
31925         /**
31926          * @event beforehide
31927          * Fires before this dialog is hidden.
31928          * @param {Roo.BasicDialog} this
31929          */
31930         "beforehide" : true,
31931         /**
31932          * @event hide
31933          * Fires when this dialog is hidden.
31934          * @param {Roo.BasicDialog} this
31935          */
31936         "hide" : true,
31937         /**
31938          * @event beforeshow
31939          * Fires before this dialog is shown.
31940          * @param {Roo.BasicDialog} this
31941          */
31942         "beforeshow" : true,
31943         /**
31944          * @event show
31945          * Fires when this dialog is shown.
31946          * @param {Roo.BasicDialog} this
31947          */
31948         "show" : true
31949     });
31950     el.on("keydown", this.onKeyDown, this);
31951     el.on("mousedown", this.toFront, this);
31952     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31953     this.el.hide();
31954     Roo.DialogManager.register(this);
31955     Roo.BasicDialog.superclass.constructor.call(this);
31956 };
31957
31958 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31959     shadowOffset: Roo.isIE ? 6 : 5,
31960     minHeight: 80,
31961     minWidth: 200,
31962     minButtonWidth: 75,
31963     defaultButton: null,
31964     buttonAlign: "right",
31965     tabTag: 'div',
31966     firstShow: true,
31967
31968     /**
31969      * Sets the dialog title text
31970      * @param {String} text The title text to display
31971      * @return {Roo.BasicDialog} this
31972      */
31973     setTitle : function(text){
31974         this.header.update(text);
31975         return this;
31976     },
31977
31978     // private
31979     closeClick : function(){
31980         this.hide();
31981     },
31982
31983     // private
31984     collapseClick : function(){
31985         this[this.collapsed ? "expand" : "collapse"]();
31986     },
31987
31988     /**
31989      * Collapses the dialog to its minimized state (only the title bar is visible).
31990      * Equivalent to the user clicking the collapse dialog button.
31991      */
31992     collapse : function(){
31993         if(!this.collapsed){
31994             this.collapsed = true;
31995             this.el.addClass("x-dlg-collapsed");
31996             this.restoreHeight = this.el.getHeight();
31997             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31998         }
31999     },
32000
32001     /**
32002      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32003      * clicking the expand dialog button.
32004      */
32005     expand : function(){
32006         if(this.collapsed){
32007             this.collapsed = false;
32008             this.el.removeClass("x-dlg-collapsed");
32009             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32010         }
32011     },
32012
32013     /**
32014      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32015      * @return {Roo.TabPanel} The tabs component
32016      */
32017     initTabs : function(){
32018         var tabs = this.getTabs();
32019         while(tabs.getTab(0)){
32020             tabs.removeTab(0);
32021         }
32022         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32023             var dom = el.dom;
32024             tabs.addTab(Roo.id(dom), dom.title);
32025             dom.title = "";
32026         });
32027         tabs.activate(0);
32028         return tabs;
32029     },
32030
32031     // private
32032     beforeResize : function(){
32033         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32034     },
32035
32036     // private
32037     onResize : function(){
32038         this.refreshSize();
32039         this.syncBodyHeight();
32040         this.adjustAssets();
32041         this.focus();
32042         this.fireEvent("resize", this, this.size.width, this.size.height);
32043     },
32044
32045     // private
32046     onKeyDown : function(e){
32047         if(this.isVisible()){
32048             this.fireEvent("keydown", this, e);
32049         }
32050     },
32051
32052     /**
32053      * Resizes the dialog.
32054      * @param {Number} width
32055      * @param {Number} height
32056      * @return {Roo.BasicDialog} this
32057      */
32058     resizeTo : function(width, height){
32059         this.el.setSize(width, height);
32060         this.size = {width: width, height: height};
32061         this.syncBodyHeight();
32062         if(this.fixedcenter){
32063             this.center();
32064         }
32065         if(this.isVisible()){
32066             this.constrainXY();
32067             this.adjustAssets();
32068         }
32069         this.fireEvent("resize", this, width, height);
32070         return this;
32071     },
32072
32073
32074     /**
32075      * Resizes the dialog to fit the specified content size.
32076      * @param {Number} width
32077      * @param {Number} height
32078      * @return {Roo.BasicDialog} this
32079      */
32080     setContentSize : function(w, h){
32081         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32082         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32083         //if(!this.el.isBorderBox()){
32084             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32085             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32086         //}
32087         if(this.tabs){
32088             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32089             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32090         }
32091         this.resizeTo(w, h);
32092         return this;
32093     },
32094
32095     /**
32096      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32097      * executed in response to a particular key being pressed while the dialog is active.
32098      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32099      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32100      * @param {Function} fn The function to call
32101      * @param {Object} scope (optional) The scope of the function
32102      * @return {Roo.BasicDialog} this
32103      */
32104     addKeyListener : function(key, fn, scope){
32105         var keyCode, shift, ctrl, alt;
32106         if(typeof key == "object" && !(key instanceof Array)){
32107             keyCode = key["key"];
32108             shift = key["shift"];
32109             ctrl = key["ctrl"];
32110             alt = key["alt"];
32111         }else{
32112             keyCode = key;
32113         }
32114         var handler = function(dlg, e){
32115             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32116                 var k = e.getKey();
32117                 if(keyCode instanceof Array){
32118                     for(var i = 0, len = keyCode.length; i < len; i++){
32119                         if(keyCode[i] == k){
32120                           fn.call(scope || window, dlg, k, e);
32121                           return;
32122                         }
32123                     }
32124                 }else{
32125                     if(k == keyCode){
32126                         fn.call(scope || window, dlg, k, e);
32127                     }
32128                 }
32129             }
32130         };
32131         this.on("keydown", handler);
32132         return this;
32133     },
32134
32135     /**
32136      * Returns the TabPanel component (creates it if it doesn't exist).
32137      * Note: If you wish to simply check for the existence of tabs without creating them,
32138      * check for a null 'tabs' property.
32139      * @return {Roo.TabPanel} The tabs component
32140      */
32141     getTabs : function(){
32142         if(!this.tabs){
32143             this.el.addClass("x-dlg-auto-tabs");
32144             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32145             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32146         }
32147         return this.tabs;
32148     },
32149
32150     /**
32151      * Adds a button to the footer section of the dialog.
32152      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32153      * object or a valid Roo.DomHelper element config
32154      * @param {Function} handler The function called when the button is clicked
32155      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32156      * @return {Roo.Button} The new button
32157      */
32158     addButton : function(config, handler, scope){
32159         var dh = Roo.DomHelper;
32160         if(!this.footer){
32161             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32162         }
32163         if(!this.btnContainer){
32164             var tb = this.footer.createChild({
32165
32166                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32167                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32168             }, null, true);
32169             this.btnContainer = tb.firstChild.firstChild.firstChild;
32170         }
32171         var bconfig = {
32172             handler: handler,
32173             scope: scope,
32174             minWidth: this.minButtonWidth,
32175             hideParent:true
32176         };
32177         if(typeof config == "string"){
32178             bconfig.text = config;
32179         }else{
32180             if(config.tag){
32181                 bconfig.dhconfig = config;
32182             }else{
32183                 Roo.apply(bconfig, config);
32184             }
32185         }
32186         var fc = false;
32187         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32188             bconfig.position = Math.max(0, bconfig.position);
32189             fc = this.btnContainer.childNodes[bconfig.position];
32190         }
32191          
32192         var btn = new Roo.Button(
32193             fc ? 
32194                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32195                 : this.btnContainer.appendChild(document.createElement("td")),
32196             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32197             bconfig
32198         );
32199         this.syncBodyHeight();
32200         if(!this.buttons){
32201             /**
32202              * Array of all the buttons that have been added to this dialog via addButton
32203              * @type Array
32204              */
32205             this.buttons = [];
32206         }
32207         this.buttons.push(btn);
32208         return btn;
32209     },
32210
32211     /**
32212      * Sets the default button to be focused when the dialog is displayed.
32213      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32214      * @return {Roo.BasicDialog} this
32215      */
32216     setDefaultButton : function(btn){
32217         this.defaultButton = btn;
32218         return this;
32219     },
32220
32221     // private
32222     getHeaderFooterHeight : function(safe){
32223         var height = 0;
32224         if(this.header){
32225            height += this.header.getHeight();
32226         }
32227         if(this.footer){
32228            var fm = this.footer.getMargins();
32229             height += (this.footer.getHeight()+fm.top+fm.bottom);
32230         }
32231         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32232         height += this.centerBg.getPadding("tb");
32233         return height;
32234     },
32235
32236     // private
32237     syncBodyHeight : function()
32238     {
32239         var bd = this.body, // the text
32240             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32241             bw = this.bwrap;
32242         var height = this.size.height - this.getHeaderFooterHeight(false);
32243         bd.setHeight(height-bd.getMargins("tb"));
32244         var hh = this.header.getHeight();
32245         var h = this.size.height-hh;
32246         cb.setHeight(h);
32247         
32248         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32249         bw.setHeight(h-cb.getPadding("tb"));
32250         
32251         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32252         bd.setWidth(bw.getWidth(true));
32253         if(this.tabs){
32254             this.tabs.syncHeight();
32255             if(Roo.isIE){
32256                 this.tabs.el.repaint();
32257             }
32258         }
32259     },
32260
32261     /**
32262      * Restores the previous state of the dialog if Roo.state is configured.
32263      * @return {Roo.BasicDialog} this
32264      */
32265     restoreState : function(){
32266         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32267         if(box && box.width){
32268             this.xy = [box.x, box.y];
32269             this.resizeTo(box.width, box.height);
32270         }
32271         return this;
32272     },
32273
32274     // private
32275     beforeShow : function(){
32276         this.expand();
32277         if(this.fixedcenter){
32278             this.xy = this.el.getCenterXY(true);
32279         }
32280         if(this.modal){
32281             Roo.get(document.body).addClass("x-body-masked");
32282             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32283             this.mask.show();
32284         }
32285         this.constrainXY();
32286     },
32287
32288     // private
32289     animShow : function(){
32290         var b = Roo.get(this.animateTarget).getBox();
32291         this.proxy.setSize(b.width, b.height);
32292         this.proxy.setLocation(b.x, b.y);
32293         this.proxy.show();
32294         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32295                     true, .35, this.showEl.createDelegate(this));
32296     },
32297
32298     /**
32299      * Shows the dialog.
32300      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32301      * @return {Roo.BasicDialog} this
32302      */
32303     show : function(animateTarget){
32304         if (this.fireEvent("beforeshow", this) === false){
32305             return;
32306         }
32307         if(this.syncHeightBeforeShow){
32308             this.syncBodyHeight();
32309         }else if(this.firstShow){
32310             this.firstShow = false;
32311             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32312         }
32313         this.animateTarget = animateTarget || this.animateTarget;
32314         if(!this.el.isVisible()){
32315             this.beforeShow();
32316             if(this.animateTarget && Roo.get(this.animateTarget)){
32317                 this.animShow();
32318             }else{
32319                 this.showEl();
32320             }
32321         }
32322         return this;
32323     },
32324
32325     // private
32326     showEl : function(){
32327         this.proxy.hide();
32328         this.el.setXY(this.xy);
32329         this.el.show();
32330         this.adjustAssets(true);
32331         this.toFront();
32332         this.focus();
32333         // IE peekaboo bug - fix found by Dave Fenwick
32334         if(Roo.isIE){
32335             this.el.repaint();
32336         }
32337         this.fireEvent("show", this);
32338     },
32339
32340     /**
32341      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32342      * dialog itself will receive focus.
32343      */
32344     focus : function(){
32345         if(this.defaultButton){
32346             this.defaultButton.focus();
32347         }else{
32348             this.focusEl.focus();
32349         }
32350     },
32351
32352     // private
32353     constrainXY : function(){
32354         if(this.constraintoviewport !== false){
32355             if(!this.viewSize){
32356                 if(this.container){
32357                     var s = this.container.getSize();
32358                     this.viewSize = [s.width, s.height];
32359                 }else{
32360                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32361                 }
32362             }
32363             var s = Roo.get(this.container||document).getScroll();
32364
32365             var x = this.xy[0], y = this.xy[1];
32366             var w = this.size.width, h = this.size.height;
32367             var vw = this.viewSize[0], vh = this.viewSize[1];
32368             // only move it if it needs it
32369             var moved = false;
32370             // first validate right/bottom
32371             if(x + w > vw+s.left){
32372                 x = vw - w;
32373                 moved = true;
32374             }
32375             if(y + h > vh+s.top){
32376                 y = vh - h;
32377                 moved = true;
32378             }
32379             // then make sure top/left isn't negative
32380             if(x < s.left){
32381                 x = s.left;
32382                 moved = true;
32383             }
32384             if(y < s.top){
32385                 y = s.top;
32386                 moved = true;
32387             }
32388             if(moved){
32389                 // cache xy
32390                 this.xy = [x, y];
32391                 if(this.isVisible()){
32392                     this.el.setLocation(x, y);
32393                     this.adjustAssets();
32394                 }
32395             }
32396         }
32397     },
32398
32399     // private
32400     onDrag : function(){
32401         if(!this.proxyDrag){
32402             this.xy = this.el.getXY();
32403             this.adjustAssets();
32404         }
32405     },
32406
32407     // private
32408     adjustAssets : function(doShow){
32409         var x = this.xy[0], y = this.xy[1];
32410         var w = this.size.width, h = this.size.height;
32411         if(doShow === true){
32412             if(this.shadow){
32413                 this.shadow.show(this.el);
32414             }
32415             if(this.shim){
32416                 this.shim.show();
32417             }
32418         }
32419         if(this.shadow && this.shadow.isVisible()){
32420             this.shadow.show(this.el);
32421         }
32422         if(this.shim && this.shim.isVisible()){
32423             this.shim.setBounds(x, y, w, h);
32424         }
32425     },
32426
32427     // private
32428     adjustViewport : function(w, h){
32429         if(!w || !h){
32430             w = Roo.lib.Dom.getViewWidth();
32431             h = Roo.lib.Dom.getViewHeight();
32432         }
32433         // cache the size
32434         this.viewSize = [w, h];
32435         if(this.modal && this.mask.isVisible()){
32436             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32437             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32438         }
32439         if(this.isVisible()){
32440             this.constrainXY();
32441         }
32442     },
32443
32444     /**
32445      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32446      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32447      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32448      */
32449     destroy : function(removeEl){
32450         if(this.isVisible()){
32451             this.animateTarget = null;
32452             this.hide();
32453         }
32454         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32455         if(this.tabs){
32456             this.tabs.destroy(removeEl);
32457         }
32458         Roo.destroy(
32459              this.shim,
32460              this.proxy,
32461              this.resizer,
32462              this.close,
32463              this.mask
32464         );
32465         if(this.dd){
32466             this.dd.unreg();
32467         }
32468         if(this.buttons){
32469            for(var i = 0, len = this.buttons.length; i < len; i++){
32470                this.buttons[i].destroy();
32471            }
32472         }
32473         this.el.removeAllListeners();
32474         if(removeEl === true){
32475             this.el.update("");
32476             this.el.remove();
32477         }
32478         Roo.DialogManager.unregister(this);
32479     },
32480
32481     // private
32482     startMove : function(){
32483         if(this.proxyDrag){
32484             this.proxy.show();
32485         }
32486         if(this.constraintoviewport !== false){
32487             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32488         }
32489     },
32490
32491     // private
32492     endMove : function(){
32493         if(!this.proxyDrag){
32494             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32495         }else{
32496             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32497             this.proxy.hide();
32498         }
32499         this.refreshSize();
32500         this.adjustAssets();
32501         this.focus();
32502         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32503     },
32504
32505     /**
32506      * Brings this dialog to the front of any other visible dialogs
32507      * @return {Roo.BasicDialog} this
32508      */
32509     toFront : function(){
32510         Roo.DialogManager.bringToFront(this);
32511         return this;
32512     },
32513
32514     /**
32515      * Sends this dialog to the back (under) of any other visible dialogs
32516      * @return {Roo.BasicDialog} this
32517      */
32518     toBack : function(){
32519         Roo.DialogManager.sendToBack(this);
32520         return this;
32521     },
32522
32523     /**
32524      * Centers this dialog in the viewport
32525      * @return {Roo.BasicDialog} this
32526      */
32527     center : function(){
32528         var xy = this.el.getCenterXY(true);
32529         this.moveTo(xy[0], xy[1]);
32530         return this;
32531     },
32532
32533     /**
32534      * Moves the dialog's top-left corner to the specified point
32535      * @param {Number} x
32536      * @param {Number} y
32537      * @return {Roo.BasicDialog} this
32538      */
32539     moveTo : function(x, y){
32540         this.xy = [x,y];
32541         if(this.isVisible()){
32542             this.el.setXY(this.xy);
32543             this.adjustAssets();
32544         }
32545         return this;
32546     },
32547
32548     /**
32549      * Aligns the dialog to the specified element
32550      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32551      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32552      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32553      * @return {Roo.BasicDialog} this
32554      */
32555     alignTo : function(element, position, offsets){
32556         this.xy = this.el.getAlignToXY(element, position, offsets);
32557         if(this.isVisible()){
32558             this.el.setXY(this.xy);
32559             this.adjustAssets();
32560         }
32561         return this;
32562     },
32563
32564     /**
32565      * Anchors an element to another element and realigns it when the window is resized.
32566      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32567      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32568      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32569      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32570      * is a number, it is used as the buffer delay (defaults to 50ms).
32571      * @return {Roo.BasicDialog} this
32572      */
32573     anchorTo : function(el, alignment, offsets, monitorScroll){
32574         var action = function(){
32575             this.alignTo(el, alignment, offsets);
32576         };
32577         Roo.EventManager.onWindowResize(action, this);
32578         var tm = typeof monitorScroll;
32579         if(tm != 'undefined'){
32580             Roo.EventManager.on(window, 'scroll', action, this,
32581                 {buffer: tm == 'number' ? monitorScroll : 50});
32582         }
32583         action.call(this);
32584         return this;
32585     },
32586
32587     /**
32588      * Returns true if the dialog is visible
32589      * @return {Boolean}
32590      */
32591     isVisible : function(){
32592         return this.el.isVisible();
32593     },
32594
32595     // private
32596     animHide : function(callback){
32597         var b = Roo.get(this.animateTarget).getBox();
32598         this.proxy.show();
32599         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32600         this.el.hide();
32601         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32602                     this.hideEl.createDelegate(this, [callback]));
32603     },
32604
32605     /**
32606      * Hides the dialog.
32607      * @param {Function} callback (optional) Function to call when the dialog is hidden
32608      * @return {Roo.BasicDialog} this
32609      */
32610     hide : function(callback){
32611         if (this.fireEvent("beforehide", this) === false){
32612             return;
32613         }
32614         if(this.shadow){
32615             this.shadow.hide();
32616         }
32617         if(this.shim) {
32618           this.shim.hide();
32619         }
32620         // sometimes animateTarget seems to get set.. causing problems...
32621         // this just double checks..
32622         if(this.animateTarget && Roo.get(this.animateTarget)) {
32623            this.animHide(callback);
32624         }else{
32625             this.el.hide();
32626             this.hideEl(callback);
32627         }
32628         return this;
32629     },
32630
32631     // private
32632     hideEl : function(callback){
32633         this.proxy.hide();
32634         if(this.modal){
32635             this.mask.hide();
32636             Roo.get(document.body).removeClass("x-body-masked");
32637         }
32638         this.fireEvent("hide", this);
32639         if(typeof callback == "function"){
32640             callback();
32641         }
32642     },
32643
32644     // private
32645     hideAction : function(){
32646         this.setLeft("-10000px");
32647         this.setTop("-10000px");
32648         this.setStyle("visibility", "hidden");
32649     },
32650
32651     // private
32652     refreshSize : function(){
32653         this.size = this.el.getSize();
32654         this.xy = this.el.getXY();
32655         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32656     },
32657
32658     // private
32659     // z-index is managed by the DialogManager and may be overwritten at any time
32660     setZIndex : function(index){
32661         if(this.modal){
32662             this.mask.setStyle("z-index", index);
32663         }
32664         if(this.shim){
32665             this.shim.setStyle("z-index", ++index);
32666         }
32667         if(this.shadow){
32668             this.shadow.setZIndex(++index);
32669         }
32670         this.el.setStyle("z-index", ++index);
32671         if(this.proxy){
32672             this.proxy.setStyle("z-index", ++index);
32673         }
32674         if(this.resizer){
32675             this.resizer.proxy.setStyle("z-index", ++index);
32676         }
32677
32678         this.lastZIndex = index;
32679     },
32680
32681     /**
32682      * Returns the element for this dialog
32683      * @return {Roo.Element} The underlying dialog Element
32684      */
32685     getEl : function(){
32686         return this.el;
32687     }
32688 });
32689
32690 /**
32691  * @class Roo.DialogManager
32692  * Provides global access to BasicDialogs that have been created and
32693  * support for z-indexing (layering) multiple open dialogs.
32694  */
32695 Roo.DialogManager = function(){
32696     var list = {};
32697     var accessList = [];
32698     var front = null;
32699
32700     // private
32701     var sortDialogs = function(d1, d2){
32702         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32703     };
32704
32705     // private
32706     var orderDialogs = function(){
32707         accessList.sort(sortDialogs);
32708         var seed = Roo.DialogManager.zseed;
32709         for(var i = 0, len = accessList.length; i < len; i++){
32710             var dlg = accessList[i];
32711             if(dlg){
32712                 dlg.setZIndex(seed + (i*10));
32713             }
32714         }
32715     };
32716
32717     return {
32718         /**
32719          * The starting z-index for BasicDialogs (defaults to 9000)
32720          * @type Number The z-index value
32721          */
32722         zseed : 9000,
32723
32724         // private
32725         register : function(dlg){
32726             list[dlg.id] = dlg;
32727             accessList.push(dlg);
32728         },
32729
32730         // private
32731         unregister : function(dlg){
32732             delete list[dlg.id];
32733             var i=0;
32734             var len=0;
32735             if(!accessList.indexOf){
32736                 for(  i = 0, len = accessList.length; i < len; i++){
32737                     if(accessList[i] == dlg){
32738                         accessList.splice(i, 1);
32739                         return;
32740                     }
32741                 }
32742             }else{
32743                  i = accessList.indexOf(dlg);
32744                 if(i != -1){
32745                     accessList.splice(i, 1);
32746                 }
32747             }
32748         },
32749
32750         /**
32751          * Gets a registered dialog by id
32752          * @param {String/Object} id The id of the dialog or a dialog
32753          * @return {Roo.BasicDialog} this
32754          */
32755         get : function(id){
32756             return typeof id == "object" ? id : list[id];
32757         },
32758
32759         /**
32760          * Brings the specified dialog to the front
32761          * @param {String/Object} dlg The id of the dialog or a dialog
32762          * @return {Roo.BasicDialog} this
32763          */
32764         bringToFront : function(dlg){
32765             dlg = this.get(dlg);
32766             if(dlg != front){
32767                 front = dlg;
32768                 dlg._lastAccess = new Date().getTime();
32769                 orderDialogs();
32770             }
32771             return dlg;
32772         },
32773
32774         /**
32775          * Sends the specified dialog to the back
32776          * @param {String/Object} dlg The id of the dialog or a dialog
32777          * @return {Roo.BasicDialog} this
32778          */
32779         sendToBack : function(dlg){
32780             dlg = this.get(dlg);
32781             dlg._lastAccess = -(new Date().getTime());
32782             orderDialogs();
32783             return dlg;
32784         },
32785
32786         /**
32787          * Hides all dialogs
32788          */
32789         hideAll : function(){
32790             for(var id in list){
32791                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32792                     list[id].hide();
32793                 }
32794             }
32795         }
32796     };
32797 }();
32798
32799 /**
32800  * @class Roo.LayoutDialog
32801  * @extends Roo.BasicDialog
32802  * Dialog which provides adjustments for working with a layout in a Dialog.
32803  * Add your necessary layout config options to the dialog's config.<br>
32804  * Example usage (including a nested layout):
32805  * <pre><code>
32806 if(!dialog){
32807     dialog = new Roo.LayoutDialog("download-dlg", {
32808         modal: true,
32809         width:600,
32810         height:450,
32811         shadow:true,
32812         minWidth:500,
32813         minHeight:350,
32814         autoTabs:true,
32815         proxyDrag:true,
32816         // layout config merges with the dialog config
32817         center:{
32818             tabPosition: "top",
32819             alwaysShowTabs: true
32820         }
32821     });
32822     dialog.addKeyListener(27, dialog.hide, dialog);
32823     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32824     dialog.addButton("Build It!", this.getDownload, this);
32825
32826     // we can even add nested layouts
32827     var innerLayout = new Roo.BorderLayout("dl-inner", {
32828         east: {
32829             initialSize: 200,
32830             autoScroll:true,
32831             split:true
32832         },
32833         center: {
32834             autoScroll:true
32835         }
32836     });
32837     innerLayout.beginUpdate();
32838     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32839     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32840     innerLayout.endUpdate(true);
32841
32842     var layout = dialog.getLayout();
32843     layout.beginUpdate();
32844     layout.add("center", new Roo.ContentPanel("standard-panel",
32845                         {title: "Download the Source", fitToFrame:true}));
32846     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32847                {title: "Build your own roo.js"}));
32848     layout.getRegion("center").showPanel(sp);
32849     layout.endUpdate();
32850 }
32851 </code></pre>
32852     * @constructor
32853     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32854     * @param {Object} config configuration options
32855   */
32856 Roo.LayoutDialog = function(el, cfg){
32857     
32858     var config=  cfg;
32859     if (typeof(cfg) == 'undefined') {
32860         config = Roo.apply({}, el);
32861         // not sure why we use documentElement here.. - it should always be body.
32862         // IE7 borks horribly if we use documentElement.
32863         // webkit also does not like documentElement - it creates a body element...
32864         el = Roo.get( document.body || document.documentElement ).createChild();
32865         //config.autoCreate = true;
32866     }
32867     
32868     
32869     config.autoTabs = false;
32870     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32871     this.body.setStyle({overflow:"hidden", position:"relative"});
32872     this.layout = new Roo.BorderLayout(this.body.dom, config);
32873     this.layout.monitorWindowResize = false;
32874     this.el.addClass("x-dlg-auto-layout");
32875     // fix case when center region overwrites center function
32876     this.center = Roo.BasicDialog.prototype.center;
32877     this.on("show", this.layout.layout, this.layout, true);
32878     if (config.items) {
32879         var xitems = config.items;
32880         delete config.items;
32881         Roo.each(xitems, this.addxtype, this);
32882     }
32883     
32884     
32885 };
32886 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32887     /**
32888      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32889      * @deprecated
32890      */
32891     endUpdate : function(){
32892         this.layout.endUpdate();
32893     },
32894
32895     /**
32896      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32897      *  @deprecated
32898      */
32899     beginUpdate : function(){
32900         this.layout.beginUpdate();
32901     },
32902
32903     /**
32904      * Get the BorderLayout for this dialog
32905      * @return {Roo.BorderLayout}
32906      */
32907     getLayout : function(){
32908         return this.layout;
32909     },
32910
32911     showEl : function(){
32912         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32913         if(Roo.isIE7){
32914             this.layout.layout();
32915         }
32916     },
32917
32918     // private
32919     // Use the syncHeightBeforeShow config option to control this automatically
32920     syncBodyHeight : function(){
32921         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32922         if(this.layout){this.layout.layout();}
32923     },
32924     
32925       /**
32926      * Add an xtype element (actually adds to the layout.)
32927      * @return {Object} xdata xtype object data.
32928      */
32929     
32930     addxtype : function(c) {
32931         return this.layout.addxtype(c);
32932     }
32933 });/*
32934  * Based on:
32935  * Ext JS Library 1.1.1
32936  * Copyright(c) 2006-2007, Ext JS, LLC.
32937  *
32938  * Originally Released Under LGPL - original licence link has changed is not relivant.
32939  *
32940  * Fork - LGPL
32941  * <script type="text/javascript">
32942  */
32943  
32944 /**
32945  * @class Roo.MessageBox
32946  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32947  * Example usage:
32948  *<pre><code>
32949 // Basic alert:
32950 Roo.Msg.alert('Status', 'Changes saved successfully.');
32951
32952 // Prompt for user data:
32953 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32954     if (btn == 'ok'){
32955         // process text value...
32956     }
32957 });
32958
32959 // Show a dialog using config options:
32960 Roo.Msg.show({
32961    title:'Save Changes?',
32962    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32963    buttons: Roo.Msg.YESNOCANCEL,
32964    fn: processResult,
32965    animEl: 'elId'
32966 });
32967 </code></pre>
32968  * @singleton
32969  */
32970 Roo.MessageBox = function(){
32971     var dlg, opt, mask, waitTimer;
32972     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32973     var buttons, activeTextEl, bwidth;
32974
32975     // private
32976     var handleButton = function(button){
32977         dlg.hide();
32978         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32979     };
32980
32981     // private
32982     var handleHide = function(){
32983         if(opt && opt.cls){
32984             dlg.el.removeClass(opt.cls);
32985         }
32986         if(waitTimer){
32987             Roo.TaskMgr.stop(waitTimer);
32988             waitTimer = null;
32989         }
32990     };
32991
32992     // private
32993     var updateButtons = function(b){
32994         var width = 0;
32995         if(!b){
32996             buttons["ok"].hide();
32997             buttons["cancel"].hide();
32998             buttons["yes"].hide();
32999             buttons["no"].hide();
33000             dlg.footer.dom.style.display = 'none';
33001             return width;
33002         }
33003         dlg.footer.dom.style.display = '';
33004         for(var k in buttons){
33005             if(typeof buttons[k] != "function"){
33006                 if(b[k]){
33007                     buttons[k].show();
33008                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33009                     width += buttons[k].el.getWidth()+15;
33010                 }else{
33011                     buttons[k].hide();
33012                 }
33013             }
33014         }
33015         return width;
33016     };
33017
33018     // private
33019     var handleEsc = function(d, k, e){
33020         if(opt && opt.closable !== false){
33021             dlg.hide();
33022         }
33023         if(e){
33024             e.stopEvent();
33025         }
33026     };
33027
33028     return {
33029         /**
33030          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33031          * @return {Roo.BasicDialog} The BasicDialog element
33032          */
33033         getDialog : function(){
33034            if(!dlg){
33035                 dlg = new Roo.BasicDialog("x-msg-box", {
33036                     autoCreate : true,
33037                     shadow: true,
33038                     draggable: true,
33039                     resizable:false,
33040                     constraintoviewport:false,
33041                     fixedcenter:true,
33042                     collapsible : false,
33043                     shim:true,
33044                     modal: true,
33045                     width:400, height:100,
33046                     buttonAlign:"center",
33047                     closeClick : function(){
33048                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33049                             handleButton("no");
33050                         }else{
33051                             handleButton("cancel");
33052                         }
33053                     }
33054                 });
33055                 dlg.on("hide", handleHide);
33056                 mask = dlg.mask;
33057                 dlg.addKeyListener(27, handleEsc);
33058                 buttons = {};
33059                 var bt = this.buttonText;
33060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33064                 bodyEl = dlg.body.createChild({
33065
33066                     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>'
33067                 });
33068                 msgEl = bodyEl.dom.firstChild;
33069                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33070                 textboxEl.enableDisplayMode();
33071                 textboxEl.addKeyListener([10,13], function(){
33072                     if(dlg.isVisible() && opt && opt.buttons){
33073                         if(opt.buttons.ok){
33074                             handleButton("ok");
33075                         }else if(opt.buttons.yes){
33076                             handleButton("yes");
33077                         }
33078                     }
33079                 });
33080                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33081                 textareaEl.enableDisplayMode();
33082                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33083                 progressEl.enableDisplayMode();
33084                 var pf = progressEl.dom.firstChild;
33085                 if (pf) {
33086                     pp = Roo.get(pf.firstChild);
33087                     pp.setHeight(pf.offsetHeight);
33088                 }
33089                 
33090             }
33091             return dlg;
33092         },
33093
33094         /**
33095          * Updates the message box body text
33096          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33097          * the XHTML-compliant non-breaking space character '&amp;#160;')
33098          * @return {Roo.MessageBox} This message box
33099          */
33100         updateText : function(text){
33101             if(!dlg.isVisible() && !opt.width){
33102                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33103             }
33104             msgEl.innerHTML = text || '&#160;';
33105       
33106             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33107             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33108             var w = Math.max(
33109                     Math.min(opt.width || cw , this.maxWidth), 
33110                     Math.max(opt.minWidth || this.minWidth, bwidth)
33111             );
33112             if(opt.prompt){
33113                 activeTextEl.setWidth(w);
33114             }
33115             if(dlg.isVisible()){
33116                 dlg.fixedcenter = false;
33117             }
33118             // to big, make it scroll. = But as usual stupid IE does not support
33119             // !important..
33120             
33121             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33122                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33123                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33124             } else {
33125                 bodyEl.dom.style.height = '';
33126                 bodyEl.dom.style.overflowY = '';
33127             }
33128             if (cw > w) {
33129                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33130             } else {
33131                 bodyEl.dom.style.overflowX = '';
33132             }
33133             
33134             dlg.setContentSize(w, bodyEl.getHeight());
33135             if(dlg.isVisible()){
33136                 dlg.fixedcenter = true;
33137             }
33138             return this;
33139         },
33140
33141         /**
33142          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33143          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33144          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33145          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33146          * @return {Roo.MessageBox} This message box
33147          */
33148         updateProgress : function(value, text){
33149             if(text){
33150                 this.updateText(text);
33151             }
33152             if (pp) { // weird bug on my firefox - for some reason this is not defined
33153                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33154             }
33155             return this;
33156         },        
33157
33158         /**
33159          * Returns true if the message box is currently displayed
33160          * @return {Boolean} True if the message box is visible, else false
33161          */
33162         isVisible : function(){
33163             return dlg && dlg.isVisible();  
33164         },
33165
33166         /**
33167          * Hides the message box if it is displayed
33168          */
33169         hide : function(){
33170             if(this.isVisible()){
33171                 dlg.hide();
33172             }  
33173         },
33174
33175         /**
33176          * Displays a new message box, or reinitializes an existing message box, based on the config options
33177          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33178          * The following config object properties are supported:
33179          * <pre>
33180 Property    Type             Description
33181 ----------  ---------------  ------------------------------------------------------------------------------------
33182 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33183                                    closes (defaults to undefined)
33184 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33185                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33186 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33187                                    progress and wait dialogs will ignore this property and always hide the
33188                                    close button as they can only be closed programmatically.
33189 cls               String           A custom CSS class to apply to the message box element
33190 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33191                                    displayed (defaults to 75)
33192 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33193                                    function will be btn (the name of the button that was clicked, if applicable,
33194                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33195                                    Progress and wait dialogs will ignore this option since they do not respond to
33196                                    user actions and can only be closed programmatically, so any required function
33197                                    should be called by the same code after it closes the dialog.
33198 icon              String           A CSS class that provides a background image to be used as an icon for
33199                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33200 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33201 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33202 modal             Boolean          False to allow user interaction with the page while the message box is
33203                                    displayed (defaults to true)
33204 msg               String           A string that will replace the existing message box body text (defaults
33205                                    to the XHTML-compliant non-breaking space character '&#160;')
33206 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33207 progress          Boolean          True to display a progress bar (defaults to false)
33208 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33209 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33210 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33211 title             String           The title text
33212 value             String           The string value to set into the active textbox element if displayed
33213 wait              Boolean          True to display a progress bar (defaults to false)
33214 width             Number           The width of the dialog in pixels
33215 </pre>
33216          *
33217          * Example usage:
33218          * <pre><code>
33219 Roo.Msg.show({
33220    title: 'Address',
33221    msg: 'Please enter your address:',
33222    width: 300,
33223    buttons: Roo.MessageBox.OKCANCEL,
33224    multiline: true,
33225    fn: saveAddress,
33226    animEl: 'addAddressBtn'
33227 });
33228 </code></pre>
33229          * @param {Object} config Configuration options
33230          * @return {Roo.MessageBox} This message box
33231          */
33232         show : function(options)
33233         {
33234             
33235             // this causes nightmares if you show one dialog after another
33236             // especially on callbacks..
33237              
33238             if(this.isVisible()){
33239                 
33240                 this.hide();
33241                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33242                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33243                 Roo.log("New Dialog Message:" +  options.msg )
33244                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33245                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33246                 
33247             }
33248             var d = this.getDialog();
33249             opt = options;
33250             d.setTitle(opt.title || "&#160;");
33251             d.close.setDisplayed(opt.closable !== false);
33252             activeTextEl = textboxEl;
33253             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33254             if(opt.prompt){
33255                 if(opt.multiline){
33256                     textboxEl.hide();
33257                     textareaEl.show();
33258                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33259                         opt.multiline : this.defaultTextHeight);
33260                     activeTextEl = textareaEl;
33261                 }else{
33262                     textboxEl.show();
33263                     textareaEl.hide();
33264                 }
33265             }else{
33266                 textboxEl.hide();
33267                 textareaEl.hide();
33268             }
33269             progressEl.setDisplayed(opt.progress === true);
33270             this.updateProgress(0);
33271             activeTextEl.dom.value = opt.value || "";
33272             if(opt.prompt){
33273                 dlg.setDefaultButton(activeTextEl);
33274             }else{
33275                 var bs = opt.buttons;
33276                 var db = null;
33277                 if(bs && bs.ok){
33278                     db = buttons["ok"];
33279                 }else if(bs && bs.yes){
33280                     db = buttons["yes"];
33281                 }
33282                 dlg.setDefaultButton(db);
33283             }
33284             bwidth = updateButtons(opt.buttons);
33285             this.updateText(opt.msg);
33286             if(opt.cls){
33287                 d.el.addClass(opt.cls);
33288             }
33289             d.proxyDrag = opt.proxyDrag === true;
33290             d.modal = opt.modal !== false;
33291             d.mask = opt.modal !== false ? mask : false;
33292             if(!d.isVisible()){
33293                 // force it to the end of the z-index stack so it gets a cursor in FF
33294                 document.body.appendChild(dlg.el.dom);
33295                 d.animateTarget = null;
33296                 d.show(options.animEl);
33297             }
33298             return this;
33299         },
33300
33301         /**
33302          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33303          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33304          * and closing the message box when the process is complete.
33305          * @param {String} title The title bar text
33306          * @param {String} msg The message box body text
33307          * @return {Roo.MessageBox} This message box
33308          */
33309         progress : function(title, msg){
33310             this.show({
33311                 title : title,
33312                 msg : msg,
33313                 buttons: false,
33314                 progress:true,
33315                 closable:false,
33316                 minWidth: this.minProgressWidth,
33317                 modal : true
33318             });
33319             return this;
33320         },
33321
33322         /**
33323          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33324          * If a callback function is passed it will be called after the user clicks the button, and the
33325          * id of the button that was clicked will be passed as the only parameter to the callback
33326          * (could also be the top-right close button).
33327          * @param {String} title The title bar text
33328          * @param {String} msg The message box body text
33329          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33330          * @param {Object} scope (optional) The scope of the callback function
33331          * @return {Roo.MessageBox} This message box
33332          */
33333         alert : function(title, msg, fn, scope){
33334             this.show({
33335                 title : title,
33336                 msg : msg,
33337                 buttons: this.OK,
33338                 fn: fn,
33339                 scope : scope,
33340                 modal : true
33341             });
33342             return this;
33343         },
33344
33345         /**
33346          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33347          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33348          * You are responsible for closing the message box when the process is complete.
33349          * @param {String} msg The message box body text
33350          * @param {String} title (optional) The title bar text
33351          * @return {Roo.MessageBox} This message box
33352          */
33353         wait : function(msg, title){
33354             this.show({
33355                 title : title,
33356                 msg : msg,
33357                 buttons: false,
33358                 closable:false,
33359                 progress:true,
33360                 modal:true,
33361                 width:300,
33362                 wait:true
33363             });
33364             waitTimer = Roo.TaskMgr.start({
33365                 run: function(i){
33366                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33367                 },
33368                 interval: 1000
33369             });
33370             return this;
33371         },
33372
33373         /**
33374          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33375          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33376          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33377          * @param {String} title The title bar text
33378          * @param {String} msg The message box body text
33379          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33380          * @param {Object} scope (optional) The scope of the callback function
33381          * @return {Roo.MessageBox} This message box
33382          */
33383         confirm : function(title, msg, fn, scope){
33384             this.show({
33385                 title : title,
33386                 msg : msg,
33387                 buttons: this.YESNO,
33388                 fn: fn,
33389                 scope : scope,
33390                 modal : true
33391             });
33392             return this;
33393         },
33394
33395         /**
33396          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33397          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33398          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33399          * (could also be the top-right close button) and the text that was entered will be passed as the two
33400          * parameters to the callback.
33401          * @param {String} title The title bar text
33402          * @param {String} msg The message box body text
33403          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33404          * @param {Object} scope (optional) The scope of the callback function
33405          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33406          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33407          * @return {Roo.MessageBox} This message box
33408          */
33409         prompt : function(title, msg, fn, scope, multiline){
33410             this.show({
33411                 title : title,
33412                 msg : msg,
33413                 buttons: this.OKCANCEL,
33414                 fn: fn,
33415                 minWidth:250,
33416                 scope : scope,
33417                 prompt:true,
33418                 multiline: multiline,
33419                 modal : true
33420             });
33421             return this;
33422         },
33423
33424         /**
33425          * Button config that displays a single OK button
33426          * @type Object
33427          */
33428         OK : {ok:true},
33429         /**
33430          * Button config that displays Yes and No buttons
33431          * @type Object
33432          */
33433         YESNO : {yes:true, no:true},
33434         /**
33435          * Button config that displays OK and Cancel buttons
33436          * @type Object
33437          */
33438         OKCANCEL : {ok:true, cancel:true},
33439         /**
33440          * Button config that displays Yes, No and Cancel buttons
33441          * @type Object
33442          */
33443         YESNOCANCEL : {yes:true, no:true, cancel:true},
33444
33445         /**
33446          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33447          * @type Number
33448          */
33449         defaultTextHeight : 75,
33450         /**
33451          * The maximum width in pixels of the message box (defaults to 600)
33452          * @type Number
33453          */
33454         maxWidth : 600,
33455         /**
33456          * The minimum width in pixels of the message box (defaults to 100)
33457          * @type Number
33458          */
33459         minWidth : 100,
33460         /**
33461          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33462          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33463          * @type Number
33464          */
33465         minProgressWidth : 250,
33466         /**
33467          * An object containing the default button text strings that can be overriden for localized language support.
33468          * Supported properties are: ok, cancel, yes and no.
33469          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33470          * @type Object
33471          */
33472         buttonText : {
33473             ok : "OK",
33474             cancel : "Cancel",
33475             yes : "Yes",
33476             no : "No"
33477         }
33478     };
33479 }();
33480
33481 /**
33482  * Shorthand for {@link Roo.MessageBox}
33483  */
33484 Roo.Msg = Roo.MessageBox;/*
33485  * Based on:
33486  * Ext JS Library 1.1.1
33487  * Copyright(c) 2006-2007, Ext JS, LLC.
33488  *
33489  * Originally Released Under LGPL - original licence link has changed is not relivant.
33490  *
33491  * Fork - LGPL
33492  * <script type="text/javascript">
33493  */
33494 /**
33495  * @class Roo.QuickTips
33496  * Provides attractive and customizable tooltips for any element.
33497  * @singleton
33498  */
33499 Roo.QuickTips = function(){
33500     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33501     var ce, bd, xy, dd;
33502     var visible = false, disabled = true, inited = false;
33503     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33504     
33505     var onOver = function(e){
33506         if(disabled){
33507             return;
33508         }
33509         var t = e.getTarget();
33510         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33511             return;
33512         }
33513         if(ce && t == ce.el){
33514             clearTimeout(hideProc);
33515             return;
33516         }
33517         if(t && tagEls[t.id]){
33518             tagEls[t.id].el = t;
33519             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33520             return;
33521         }
33522         var ttp, et = Roo.fly(t);
33523         var ns = cfg.namespace;
33524         if(tm.interceptTitles && t.title){
33525             ttp = t.title;
33526             t.qtip = ttp;
33527             t.removeAttribute("title");
33528             e.preventDefault();
33529         }else{
33530             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33531         }
33532         if(ttp){
33533             showProc = show.defer(tm.showDelay, tm, [{
33534                 el: t, 
33535                 text: ttp, 
33536                 width: et.getAttributeNS(ns, cfg.width),
33537                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33538                 title: et.getAttributeNS(ns, cfg.title),
33539                     cls: et.getAttributeNS(ns, cfg.cls)
33540             }]);
33541         }
33542     };
33543     
33544     var onOut = function(e){
33545         clearTimeout(showProc);
33546         var t = e.getTarget();
33547         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33548             hideProc = setTimeout(hide, tm.hideDelay);
33549         }
33550     };
33551     
33552     var onMove = function(e){
33553         if(disabled){
33554             return;
33555         }
33556         xy = e.getXY();
33557         xy[1] += 18;
33558         if(tm.trackMouse && ce){
33559             el.setXY(xy);
33560         }
33561     };
33562     
33563     var onDown = function(e){
33564         clearTimeout(showProc);
33565         clearTimeout(hideProc);
33566         if(!e.within(el)){
33567             if(tm.hideOnClick){
33568                 hide();
33569                 tm.disable();
33570                 tm.enable.defer(100, tm);
33571             }
33572         }
33573     };
33574     
33575     var getPad = function(){
33576         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33577     };
33578
33579     var show = function(o){
33580         if(disabled){
33581             return;
33582         }
33583         clearTimeout(dismissProc);
33584         ce = o;
33585         if(removeCls){ // in case manually hidden
33586             el.removeClass(removeCls);
33587             removeCls = null;
33588         }
33589         if(ce.cls){
33590             el.addClass(ce.cls);
33591             removeCls = ce.cls;
33592         }
33593         if(ce.title){
33594             tipTitle.update(ce.title);
33595             tipTitle.show();
33596         }else{
33597             tipTitle.update('');
33598             tipTitle.hide();
33599         }
33600         el.dom.style.width  = tm.maxWidth+'px';
33601         //tipBody.dom.style.width = '';
33602         tipBodyText.update(o.text);
33603         var p = getPad(), w = ce.width;
33604         if(!w){
33605             var td = tipBodyText.dom;
33606             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33607             if(aw > tm.maxWidth){
33608                 w = tm.maxWidth;
33609             }else if(aw < tm.minWidth){
33610                 w = tm.minWidth;
33611             }else{
33612                 w = aw;
33613             }
33614         }
33615         //tipBody.setWidth(w);
33616         el.setWidth(parseInt(w, 10) + p);
33617         if(ce.autoHide === false){
33618             close.setDisplayed(true);
33619             if(dd){
33620                 dd.unlock();
33621             }
33622         }else{
33623             close.setDisplayed(false);
33624             if(dd){
33625                 dd.lock();
33626             }
33627         }
33628         if(xy){
33629             el.avoidY = xy[1]-18;
33630             el.setXY(xy);
33631         }
33632         if(tm.animate){
33633             el.setOpacity(.1);
33634             el.setStyle("visibility", "visible");
33635             el.fadeIn({callback: afterShow});
33636         }else{
33637             afterShow();
33638         }
33639     };
33640     
33641     var afterShow = function(){
33642         if(ce){
33643             el.show();
33644             esc.enable();
33645             if(tm.autoDismiss && ce.autoHide !== false){
33646                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33647             }
33648         }
33649     };
33650     
33651     var hide = function(noanim){
33652         clearTimeout(dismissProc);
33653         clearTimeout(hideProc);
33654         ce = null;
33655         if(el.isVisible()){
33656             esc.disable();
33657             if(noanim !== true && tm.animate){
33658                 el.fadeOut({callback: afterHide});
33659             }else{
33660                 afterHide();
33661             } 
33662         }
33663     };
33664     
33665     var afterHide = function(){
33666         el.hide();
33667         if(removeCls){
33668             el.removeClass(removeCls);
33669             removeCls = null;
33670         }
33671     };
33672     
33673     return {
33674         /**
33675         * @cfg {Number} minWidth
33676         * The minimum width of the quick tip (defaults to 40)
33677         */
33678        minWidth : 40,
33679         /**
33680         * @cfg {Number} maxWidth
33681         * The maximum width of the quick tip (defaults to 300)
33682         */
33683        maxWidth : 300,
33684         /**
33685         * @cfg {Boolean} interceptTitles
33686         * True to automatically use the element's DOM title value if available (defaults to false)
33687         */
33688        interceptTitles : false,
33689         /**
33690         * @cfg {Boolean} trackMouse
33691         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33692         */
33693        trackMouse : false,
33694         /**
33695         * @cfg {Boolean} hideOnClick
33696         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33697         */
33698        hideOnClick : true,
33699         /**
33700         * @cfg {Number} showDelay
33701         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33702         */
33703        showDelay : 500,
33704         /**
33705         * @cfg {Number} hideDelay
33706         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33707         */
33708        hideDelay : 200,
33709         /**
33710         * @cfg {Boolean} autoHide
33711         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33712         * Used in conjunction with hideDelay.
33713         */
33714        autoHide : true,
33715         /**
33716         * @cfg {Boolean}
33717         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33718         * (defaults to true).  Used in conjunction with autoDismissDelay.
33719         */
33720        autoDismiss : true,
33721         /**
33722         * @cfg {Number}
33723         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33724         */
33725        autoDismissDelay : 5000,
33726        /**
33727         * @cfg {Boolean} animate
33728         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33729         */
33730        animate : false,
33731
33732        /**
33733         * @cfg {String} title
33734         * Title text to display (defaults to '').  This can be any valid HTML markup.
33735         */
33736         title: '',
33737        /**
33738         * @cfg {String} text
33739         * Body text to display (defaults to '').  This can be any valid HTML markup.
33740         */
33741         text : '',
33742        /**
33743         * @cfg {String} cls
33744         * A CSS class to apply to the base quick tip element (defaults to '').
33745         */
33746         cls : '',
33747        /**
33748         * @cfg {Number} width
33749         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33750         * minWidth or maxWidth.
33751         */
33752         width : null,
33753
33754     /**
33755      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33756      * or display QuickTips in a page.
33757      */
33758        init : function(){
33759           tm = Roo.QuickTips;
33760           cfg = tm.tagConfig;
33761           if(!inited){
33762               if(!Roo.isReady){ // allow calling of init() before onReady
33763                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33764                   return;
33765               }
33766               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33767               el.fxDefaults = {stopFx: true};
33768               // maximum custom styling
33769               //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>');
33770               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>');              
33771               tipTitle = el.child('h3');
33772               tipTitle.enableDisplayMode("block");
33773               tipBody = el.child('div.x-tip-bd');
33774               tipBodyText = el.child('div.x-tip-bd-inner');
33775               //bdLeft = el.child('div.x-tip-bd-left');
33776               //bdRight = el.child('div.x-tip-bd-right');
33777               close = el.child('div.x-tip-close');
33778               close.enableDisplayMode("block");
33779               close.on("click", hide);
33780               var d = Roo.get(document);
33781               d.on("mousedown", onDown);
33782               d.on("mouseover", onOver);
33783               d.on("mouseout", onOut);
33784               d.on("mousemove", onMove);
33785               esc = d.addKeyListener(27, hide);
33786               esc.disable();
33787               if(Roo.dd.DD){
33788                   dd = el.initDD("default", null, {
33789                       onDrag : function(){
33790                           el.sync();  
33791                       }
33792                   });
33793                   dd.setHandleElId(tipTitle.id);
33794                   dd.lock();
33795               }
33796               inited = true;
33797           }
33798           this.enable(); 
33799        },
33800
33801     /**
33802      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33803      * are supported:
33804      * <pre>
33805 Property    Type                   Description
33806 ----------  ---------------------  ------------------------------------------------------------------------
33807 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33808      * </ul>
33809      * @param {Object} config The config object
33810      */
33811        register : function(config){
33812            var cs = config instanceof Array ? config : arguments;
33813            for(var i = 0, len = cs.length; i < len; i++) {
33814                var c = cs[i];
33815                var target = c.target;
33816                if(target){
33817                    if(target instanceof Array){
33818                        for(var j = 0, jlen = target.length; j < jlen; j++){
33819                            tagEls[target[j]] = c;
33820                        }
33821                    }else{
33822                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33823                    }
33824                }
33825            }
33826        },
33827
33828     /**
33829      * Removes this quick tip from its element and destroys it.
33830      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33831      */
33832        unregister : function(el){
33833            delete tagEls[Roo.id(el)];
33834        },
33835
33836     /**
33837      * Enable this quick tip.
33838      */
33839        enable : function(){
33840            if(inited && disabled){
33841                locks.pop();
33842                if(locks.length < 1){
33843                    disabled = false;
33844                }
33845            }
33846        },
33847
33848     /**
33849      * Disable this quick tip.
33850      */
33851        disable : function(){
33852           disabled = true;
33853           clearTimeout(showProc);
33854           clearTimeout(hideProc);
33855           clearTimeout(dismissProc);
33856           if(ce){
33857               hide(true);
33858           }
33859           locks.push(1);
33860        },
33861
33862     /**
33863      * Returns true if the quick tip is enabled, else false.
33864      */
33865        isEnabled : function(){
33866             return !disabled;
33867        },
33868
33869         // private
33870        tagConfig : {
33871            namespace : "roo", // was ext?? this may break..
33872            alt_namespace : "ext",
33873            attribute : "qtip",
33874            width : "width",
33875            target : "target",
33876            title : "qtitle",
33877            hide : "hide",
33878            cls : "qclass"
33879        }
33880    };
33881 }();
33882
33883 // backwards compat
33884 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33885  * Based on:
33886  * Ext JS Library 1.1.1
33887  * Copyright(c) 2006-2007, Ext JS, LLC.
33888  *
33889  * Originally Released Under LGPL - original licence link has changed is not relivant.
33890  *
33891  * Fork - LGPL
33892  * <script type="text/javascript">
33893  */
33894  
33895
33896 /**
33897  * @class Roo.tree.TreePanel
33898  * @extends Roo.data.Tree
33899
33900  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33901  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33902  * @cfg {Boolean} enableDD true to enable drag and drop
33903  * @cfg {Boolean} enableDrag true to enable just drag
33904  * @cfg {Boolean} enableDrop true to enable just drop
33905  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33906  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33907  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33908  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33909  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33910  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33911  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33912  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33913  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33914  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33915  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33916  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33917  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33918  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33919  * @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>
33920  * @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>
33921  * 
33922  * @constructor
33923  * @param {String/HTMLElement/Element} el The container element
33924  * @param {Object} config
33925  */
33926 Roo.tree.TreePanel = function(el, config){
33927     var root = false;
33928     var loader = false;
33929     if (config.root) {
33930         root = config.root;
33931         delete config.root;
33932     }
33933     if (config.loader) {
33934         loader = config.loader;
33935         delete config.loader;
33936     }
33937     
33938     Roo.apply(this, config);
33939     Roo.tree.TreePanel.superclass.constructor.call(this);
33940     this.el = Roo.get(el);
33941     this.el.addClass('x-tree');
33942     //console.log(root);
33943     if (root) {
33944         this.setRootNode( Roo.factory(root, Roo.tree));
33945     }
33946     if (loader) {
33947         this.loader = Roo.factory(loader, Roo.tree);
33948     }
33949    /**
33950     * Read-only. The id of the container element becomes this TreePanel's id.
33951     */
33952     this.id = this.el.id;
33953     this.addEvents({
33954         /**
33955         * @event beforeload
33956         * Fires before a node is loaded, return false to cancel
33957         * @param {Node} node The node being loaded
33958         */
33959         "beforeload" : true,
33960         /**
33961         * @event load
33962         * Fires when a node is loaded
33963         * @param {Node} node The node that was loaded
33964         */
33965         "load" : true,
33966         /**
33967         * @event textchange
33968         * Fires when the text for a node is changed
33969         * @param {Node} node The node
33970         * @param {String} text The new text
33971         * @param {String} oldText The old text
33972         */
33973         "textchange" : true,
33974         /**
33975         * @event beforeexpand
33976         * Fires before a node is expanded, return false to cancel.
33977         * @param {Node} node The node
33978         * @param {Boolean} deep
33979         * @param {Boolean} anim
33980         */
33981         "beforeexpand" : true,
33982         /**
33983         * @event beforecollapse
33984         * Fires before a node is collapsed, return false to cancel.
33985         * @param {Node} node The node
33986         * @param {Boolean} deep
33987         * @param {Boolean} anim
33988         */
33989         "beforecollapse" : true,
33990         /**
33991         * @event expand
33992         * Fires when a node is expanded
33993         * @param {Node} node The node
33994         */
33995         "expand" : true,
33996         /**
33997         * @event disabledchange
33998         * Fires when the disabled status of a node changes
33999         * @param {Node} node The node
34000         * @param {Boolean} disabled
34001         */
34002         "disabledchange" : true,
34003         /**
34004         * @event collapse
34005         * Fires when a node is collapsed
34006         * @param {Node} node The node
34007         */
34008         "collapse" : true,
34009         /**
34010         * @event beforeclick
34011         * Fires before click processing on a node. Return false to cancel the default action.
34012         * @param {Node} node The node
34013         * @param {Roo.EventObject} e The event object
34014         */
34015         "beforeclick":true,
34016         /**
34017         * @event checkchange
34018         * Fires when a node with a checkbox's checked property changes
34019         * @param {Node} this This node
34020         * @param {Boolean} checked
34021         */
34022         "checkchange":true,
34023         /**
34024         * @event click
34025         * Fires when a node is clicked
34026         * @param {Node} node The node
34027         * @param {Roo.EventObject} e The event object
34028         */
34029         "click":true,
34030         /**
34031         * @event dblclick
34032         * Fires when a node is double clicked
34033         * @param {Node} node The node
34034         * @param {Roo.EventObject} e The event object
34035         */
34036         "dblclick":true,
34037         /**
34038         * @event contextmenu
34039         * Fires when a node is right clicked
34040         * @param {Node} node The node
34041         * @param {Roo.EventObject} e The event object
34042         */
34043         "contextmenu":true,
34044         /**
34045         * @event beforechildrenrendered
34046         * Fires right before the child nodes for a node are rendered
34047         * @param {Node} node The node
34048         */
34049         "beforechildrenrendered":true,
34050         /**
34051         * @event startdrag
34052         * Fires when a node starts being dragged
34053         * @param {Roo.tree.TreePanel} this
34054         * @param {Roo.tree.TreeNode} node
34055         * @param {event} e The raw browser event
34056         */ 
34057        "startdrag" : true,
34058        /**
34059         * @event enddrag
34060         * Fires when a drag operation is complete
34061         * @param {Roo.tree.TreePanel} this
34062         * @param {Roo.tree.TreeNode} node
34063         * @param {event} e The raw browser event
34064         */
34065        "enddrag" : true,
34066        /**
34067         * @event dragdrop
34068         * Fires when a dragged node is dropped on a valid DD target
34069         * @param {Roo.tree.TreePanel} this
34070         * @param {Roo.tree.TreeNode} node
34071         * @param {DD} dd The dd it was dropped on
34072         * @param {event} e The raw browser event
34073         */
34074        "dragdrop" : true,
34075        /**
34076         * @event beforenodedrop
34077         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34078         * passed to handlers has the following properties:<br />
34079         * <ul style="padding:5px;padding-left:16px;">
34080         * <li>tree - The TreePanel</li>
34081         * <li>target - The node being targeted for the drop</li>
34082         * <li>data - The drag data from the drag source</li>
34083         * <li>point - The point of the drop - append, above or below</li>
34084         * <li>source - The drag source</li>
34085         * <li>rawEvent - Raw mouse event</li>
34086         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34087         * to be inserted by setting them on this object.</li>
34088         * <li>cancel - Set this to true to cancel the drop.</li>
34089         * </ul>
34090         * @param {Object} dropEvent
34091         */
34092        "beforenodedrop" : true,
34093        /**
34094         * @event nodedrop
34095         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34096         * passed to handlers has the following properties:<br />
34097         * <ul style="padding:5px;padding-left:16px;">
34098         * <li>tree - The TreePanel</li>
34099         * <li>target - The node being targeted for the drop</li>
34100         * <li>data - The drag data from the drag source</li>
34101         * <li>point - The point of the drop - append, above or below</li>
34102         * <li>source - The drag source</li>
34103         * <li>rawEvent - Raw mouse event</li>
34104         * <li>dropNode - Dropped node(s).</li>
34105         * </ul>
34106         * @param {Object} dropEvent
34107         */
34108        "nodedrop" : true,
34109         /**
34110         * @event nodedragover
34111         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34112         * passed to handlers has the following properties:<br />
34113         * <ul style="padding:5px;padding-left:16px;">
34114         * <li>tree - The TreePanel</li>
34115         * <li>target - The node being targeted for the drop</li>
34116         * <li>data - The drag data from the drag source</li>
34117         * <li>point - The point of the drop - append, above or below</li>
34118         * <li>source - The drag source</li>
34119         * <li>rawEvent - Raw mouse event</li>
34120         * <li>dropNode - Drop node(s) provided by the source.</li>
34121         * <li>cancel - Set this to true to signal drop not allowed.</li>
34122         * </ul>
34123         * @param {Object} dragOverEvent
34124         */
34125        "nodedragover" : true
34126         
34127     });
34128     if(this.singleExpand){
34129        this.on("beforeexpand", this.restrictExpand, this);
34130     }
34131     if (this.editor) {
34132         this.editor.tree = this;
34133         this.editor = Roo.factory(this.editor, Roo.tree);
34134     }
34135     
34136     if (this.selModel) {
34137         this.selModel = Roo.factory(this.selModel, Roo.tree);
34138     }
34139    
34140 };
34141 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34142     rootVisible : true,
34143     animate: Roo.enableFx,
34144     lines : true,
34145     enableDD : false,
34146     hlDrop : Roo.enableFx,
34147   
34148     renderer: false,
34149     
34150     rendererTip: false,
34151     // private
34152     restrictExpand : function(node){
34153         var p = node.parentNode;
34154         if(p){
34155             if(p.expandedChild && p.expandedChild.parentNode == p){
34156                 p.expandedChild.collapse();
34157             }
34158             p.expandedChild = node;
34159         }
34160     },
34161
34162     // private override
34163     setRootNode : function(node){
34164         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34165         if(!this.rootVisible){
34166             node.ui = new Roo.tree.RootTreeNodeUI(node);
34167         }
34168         return node;
34169     },
34170
34171     /**
34172      * Returns the container element for this TreePanel
34173      */
34174     getEl : function(){
34175         return this.el;
34176     },
34177
34178     /**
34179      * Returns the default TreeLoader for this TreePanel
34180      */
34181     getLoader : function(){
34182         return this.loader;
34183     },
34184
34185     /**
34186      * Expand all nodes
34187      */
34188     expandAll : function(){
34189         this.root.expand(true);
34190     },
34191
34192     /**
34193      * Collapse all nodes
34194      */
34195     collapseAll : function(){
34196         this.root.collapse(true);
34197     },
34198
34199     /**
34200      * Returns the selection model used by this TreePanel
34201      */
34202     getSelectionModel : function(){
34203         if(!this.selModel){
34204             this.selModel = new Roo.tree.DefaultSelectionModel();
34205         }
34206         return this.selModel;
34207     },
34208
34209     /**
34210      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34211      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34212      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34213      * @return {Array}
34214      */
34215     getChecked : function(a, startNode){
34216         startNode = startNode || this.root;
34217         var r = [];
34218         var f = function(){
34219             if(this.attributes.checked){
34220                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34221             }
34222         }
34223         startNode.cascade(f);
34224         return r;
34225     },
34226
34227     /**
34228      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34229      * @param {String} path
34230      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34231      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34232      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34233      */
34234     expandPath : function(path, attr, callback){
34235         attr = attr || "id";
34236         var keys = path.split(this.pathSeparator);
34237         var curNode = this.root;
34238         if(curNode.attributes[attr] != keys[1]){ // invalid root
34239             if(callback){
34240                 callback(false, null);
34241             }
34242             return;
34243         }
34244         var index = 1;
34245         var f = function(){
34246             if(++index == keys.length){
34247                 if(callback){
34248                     callback(true, curNode);
34249                 }
34250                 return;
34251             }
34252             var c = curNode.findChild(attr, keys[index]);
34253             if(!c){
34254                 if(callback){
34255                     callback(false, curNode);
34256                 }
34257                 return;
34258             }
34259             curNode = c;
34260             c.expand(false, false, f);
34261         };
34262         curNode.expand(false, false, f);
34263     },
34264
34265     /**
34266      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34267      * @param {String} path
34268      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34269      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34270      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34271      */
34272     selectPath : function(path, attr, callback){
34273         attr = attr || "id";
34274         var keys = path.split(this.pathSeparator);
34275         var v = keys.pop();
34276         if(keys.length > 0){
34277             var f = function(success, node){
34278                 if(success && node){
34279                     var n = node.findChild(attr, v);
34280                     if(n){
34281                         n.select();
34282                         if(callback){
34283                             callback(true, n);
34284                         }
34285                     }else if(callback){
34286                         callback(false, n);
34287                     }
34288                 }else{
34289                     if(callback){
34290                         callback(false, n);
34291                     }
34292                 }
34293             };
34294             this.expandPath(keys.join(this.pathSeparator), attr, f);
34295         }else{
34296             this.root.select();
34297             if(callback){
34298                 callback(true, this.root);
34299             }
34300         }
34301     },
34302
34303     getTreeEl : function(){
34304         return this.el;
34305     },
34306
34307     /**
34308      * Trigger rendering of this TreePanel
34309      */
34310     render : function(){
34311         if (this.innerCt) {
34312             return this; // stop it rendering more than once!!
34313         }
34314         
34315         this.innerCt = this.el.createChild({tag:"ul",
34316                cls:"x-tree-root-ct " +
34317                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34318
34319         if(this.containerScroll){
34320             Roo.dd.ScrollManager.register(this.el);
34321         }
34322         if((this.enableDD || this.enableDrop) && !this.dropZone){
34323            /**
34324             * The dropZone used by this tree if drop is enabled
34325             * @type Roo.tree.TreeDropZone
34326             */
34327              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34328                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34329            });
34330         }
34331         if((this.enableDD || this.enableDrag) && !this.dragZone){
34332            /**
34333             * The dragZone used by this tree if drag is enabled
34334             * @type Roo.tree.TreeDragZone
34335             */
34336             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34337                ddGroup: this.ddGroup || "TreeDD",
34338                scroll: this.ddScroll
34339            });
34340         }
34341         this.getSelectionModel().init(this);
34342         if (!this.root) {
34343             Roo.log("ROOT not set in tree");
34344             return this;
34345         }
34346         this.root.render();
34347         if(!this.rootVisible){
34348             this.root.renderChildren();
34349         }
34350         return this;
34351     }
34352 });/*
34353  * Based on:
34354  * Ext JS Library 1.1.1
34355  * Copyright(c) 2006-2007, Ext JS, LLC.
34356  *
34357  * Originally Released Under LGPL - original licence link has changed is not relivant.
34358  *
34359  * Fork - LGPL
34360  * <script type="text/javascript">
34361  */
34362  
34363
34364 /**
34365  * @class Roo.tree.DefaultSelectionModel
34366  * @extends Roo.util.Observable
34367  * The default single selection for a TreePanel.
34368  * @param {Object} cfg Configuration
34369  */
34370 Roo.tree.DefaultSelectionModel = function(cfg){
34371    this.selNode = null;
34372    
34373    
34374    
34375    this.addEvents({
34376        /**
34377         * @event selectionchange
34378         * Fires when the selected node changes
34379         * @param {DefaultSelectionModel} this
34380         * @param {TreeNode} node the new selection
34381         */
34382        "selectionchange" : true,
34383
34384        /**
34385         * @event beforeselect
34386         * Fires before the selected node changes, return false to cancel the change
34387         * @param {DefaultSelectionModel} this
34388         * @param {TreeNode} node the new selection
34389         * @param {TreeNode} node the old selection
34390         */
34391        "beforeselect" : true
34392    });
34393    
34394     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34395 };
34396
34397 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34398     init : function(tree){
34399         this.tree = tree;
34400         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34401         tree.on("click", this.onNodeClick, this);
34402     },
34403     
34404     onNodeClick : function(node, e){
34405         if (e.ctrlKey && this.selNode == node)  {
34406             this.unselect(node);
34407             return;
34408         }
34409         this.select(node);
34410     },
34411     
34412     /**
34413      * Select a node.
34414      * @param {TreeNode} node The node to select
34415      * @return {TreeNode} The selected node
34416      */
34417     select : function(node){
34418         var last = this.selNode;
34419         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34420             if(last){
34421                 last.ui.onSelectedChange(false);
34422             }
34423             this.selNode = node;
34424             node.ui.onSelectedChange(true);
34425             this.fireEvent("selectionchange", this, node, last);
34426         }
34427         return node;
34428     },
34429     
34430     /**
34431      * Deselect a node.
34432      * @param {TreeNode} node The node to unselect
34433      */
34434     unselect : function(node){
34435         if(this.selNode == node){
34436             this.clearSelections();
34437         }    
34438     },
34439     
34440     /**
34441      * Clear all selections
34442      */
34443     clearSelections : function(){
34444         var n = this.selNode;
34445         if(n){
34446             n.ui.onSelectedChange(false);
34447             this.selNode = null;
34448             this.fireEvent("selectionchange", this, null);
34449         }
34450         return n;
34451     },
34452     
34453     /**
34454      * Get the selected node
34455      * @return {TreeNode} The selected node
34456      */
34457     getSelectedNode : function(){
34458         return this.selNode;    
34459     },
34460     
34461     /**
34462      * Returns true if the node is selected
34463      * @param {TreeNode} node The node to check
34464      * @return {Boolean}
34465      */
34466     isSelected : function(node){
34467         return this.selNode == node;  
34468     },
34469
34470     /**
34471      * Selects the node above the selected node in the tree, intelligently walking the nodes
34472      * @return TreeNode The new selection
34473      */
34474     selectPrevious : function(){
34475         var s = this.selNode || this.lastSelNode;
34476         if(!s){
34477             return null;
34478         }
34479         var ps = s.previousSibling;
34480         if(ps){
34481             if(!ps.isExpanded() || ps.childNodes.length < 1){
34482                 return this.select(ps);
34483             } else{
34484                 var lc = ps.lastChild;
34485                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34486                     lc = lc.lastChild;
34487                 }
34488                 return this.select(lc);
34489             }
34490         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34491             return this.select(s.parentNode);
34492         }
34493         return null;
34494     },
34495
34496     /**
34497      * Selects the node above the selected node in the tree, intelligently walking the nodes
34498      * @return TreeNode The new selection
34499      */
34500     selectNext : function(){
34501         var s = this.selNode || this.lastSelNode;
34502         if(!s){
34503             return null;
34504         }
34505         if(s.firstChild && s.isExpanded()){
34506              return this.select(s.firstChild);
34507          }else if(s.nextSibling){
34508              return this.select(s.nextSibling);
34509          }else if(s.parentNode){
34510             var newS = null;
34511             s.parentNode.bubble(function(){
34512                 if(this.nextSibling){
34513                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34514                     return false;
34515                 }
34516             });
34517             return newS;
34518          }
34519         return null;
34520     },
34521
34522     onKeyDown : function(e){
34523         var s = this.selNode || this.lastSelNode;
34524         // undesirable, but required
34525         var sm = this;
34526         if(!s){
34527             return;
34528         }
34529         var k = e.getKey();
34530         switch(k){
34531              case e.DOWN:
34532                  e.stopEvent();
34533                  this.selectNext();
34534              break;
34535              case e.UP:
34536                  e.stopEvent();
34537                  this.selectPrevious();
34538              break;
34539              case e.RIGHT:
34540                  e.preventDefault();
34541                  if(s.hasChildNodes()){
34542                      if(!s.isExpanded()){
34543                          s.expand();
34544                      }else if(s.firstChild){
34545                          this.select(s.firstChild, e);
34546                      }
34547                  }
34548              break;
34549              case e.LEFT:
34550                  e.preventDefault();
34551                  if(s.hasChildNodes() && s.isExpanded()){
34552                      s.collapse();
34553                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34554                      this.select(s.parentNode, e);
34555                  }
34556              break;
34557         };
34558     }
34559 });
34560
34561 /**
34562  * @class Roo.tree.MultiSelectionModel
34563  * @extends Roo.util.Observable
34564  * Multi selection for a TreePanel.
34565  * @param {Object} cfg Configuration
34566  */
34567 Roo.tree.MultiSelectionModel = function(){
34568    this.selNodes = [];
34569    this.selMap = {};
34570    this.addEvents({
34571        /**
34572         * @event selectionchange
34573         * Fires when the selected nodes change
34574         * @param {MultiSelectionModel} this
34575         * @param {Array} nodes Array of the selected nodes
34576         */
34577        "selectionchange" : true
34578    });
34579    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34580    
34581 };
34582
34583 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34584     init : function(tree){
34585         this.tree = tree;
34586         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34587         tree.on("click", this.onNodeClick, this);
34588     },
34589     
34590     onNodeClick : function(node, e){
34591         this.select(node, e, e.ctrlKey);
34592     },
34593     
34594     /**
34595      * Select a node.
34596      * @param {TreeNode} node The node to select
34597      * @param {EventObject} e (optional) An event associated with the selection
34598      * @param {Boolean} keepExisting True to retain existing selections
34599      * @return {TreeNode} The selected node
34600      */
34601     select : function(node, e, keepExisting){
34602         if(keepExisting !== true){
34603             this.clearSelections(true);
34604         }
34605         if(this.isSelected(node)){
34606             this.lastSelNode = node;
34607             return node;
34608         }
34609         this.selNodes.push(node);
34610         this.selMap[node.id] = node;
34611         this.lastSelNode = node;
34612         node.ui.onSelectedChange(true);
34613         this.fireEvent("selectionchange", this, this.selNodes);
34614         return node;
34615     },
34616     
34617     /**
34618      * Deselect a node.
34619      * @param {TreeNode} node The node to unselect
34620      */
34621     unselect : function(node){
34622         if(this.selMap[node.id]){
34623             node.ui.onSelectedChange(false);
34624             var sn = this.selNodes;
34625             var index = -1;
34626             if(sn.indexOf){
34627                 index = sn.indexOf(node);
34628             }else{
34629                 for(var i = 0, len = sn.length; i < len; i++){
34630                     if(sn[i] == node){
34631                         index = i;
34632                         break;
34633                     }
34634                 }
34635             }
34636             if(index != -1){
34637                 this.selNodes.splice(index, 1);
34638             }
34639             delete this.selMap[node.id];
34640             this.fireEvent("selectionchange", this, this.selNodes);
34641         }
34642     },
34643     
34644     /**
34645      * Clear all selections
34646      */
34647     clearSelections : function(suppressEvent){
34648         var sn = this.selNodes;
34649         if(sn.length > 0){
34650             for(var i = 0, len = sn.length; i < len; i++){
34651                 sn[i].ui.onSelectedChange(false);
34652             }
34653             this.selNodes = [];
34654             this.selMap = {};
34655             if(suppressEvent !== true){
34656                 this.fireEvent("selectionchange", this, this.selNodes);
34657             }
34658         }
34659     },
34660     
34661     /**
34662      * Returns true if the node is selected
34663      * @param {TreeNode} node The node to check
34664      * @return {Boolean}
34665      */
34666     isSelected : function(node){
34667         return this.selMap[node.id] ? true : false;  
34668     },
34669     
34670     /**
34671      * Returns an array of the selected nodes
34672      * @return {Array}
34673      */
34674     getSelectedNodes : function(){
34675         return this.selNodes;    
34676     },
34677
34678     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34679
34680     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34681
34682     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34683 });/*
34684  * Based on:
34685  * Ext JS Library 1.1.1
34686  * Copyright(c) 2006-2007, Ext JS, LLC.
34687  *
34688  * Originally Released Under LGPL - original licence link has changed is not relivant.
34689  *
34690  * Fork - LGPL
34691  * <script type="text/javascript">
34692  */
34693  
34694 /**
34695  * @class Roo.tree.TreeNode
34696  * @extends Roo.data.Node
34697  * @cfg {String} text The text for this node
34698  * @cfg {Boolean} expanded true to start the node expanded
34699  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34700  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34701  * @cfg {Boolean} disabled true to start the node disabled
34702  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34703  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34704  * @cfg {String} cls A css class to be added to the node
34705  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34706  * @cfg {String} href URL of the link used for the node (defaults to #)
34707  * @cfg {String} hrefTarget target frame for the link
34708  * @cfg {String} qtip An Ext QuickTip for the node
34709  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34710  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34711  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34712  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34713  * (defaults to undefined with no checkbox rendered)
34714  * @constructor
34715  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34716  */
34717 Roo.tree.TreeNode = function(attributes){
34718     attributes = attributes || {};
34719     if(typeof attributes == "string"){
34720         attributes = {text: attributes};
34721     }
34722     this.childrenRendered = false;
34723     this.rendered = false;
34724     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34725     this.expanded = attributes.expanded === true;
34726     this.isTarget = attributes.isTarget !== false;
34727     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34728     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34729
34730     /**
34731      * Read-only. The text for this node. To change it use setText().
34732      * @type String
34733      */
34734     this.text = attributes.text;
34735     /**
34736      * True if this node is disabled.
34737      * @type Boolean
34738      */
34739     this.disabled = attributes.disabled === true;
34740
34741     this.addEvents({
34742         /**
34743         * @event textchange
34744         * Fires when the text for this node is changed
34745         * @param {Node} this This node
34746         * @param {String} text The new text
34747         * @param {String} oldText The old text
34748         */
34749         "textchange" : true,
34750         /**
34751         * @event beforeexpand
34752         * Fires before this node is expanded, return false to cancel.
34753         * @param {Node} this This node
34754         * @param {Boolean} deep
34755         * @param {Boolean} anim
34756         */
34757         "beforeexpand" : true,
34758         /**
34759         * @event beforecollapse
34760         * Fires before this node is collapsed, return false to cancel.
34761         * @param {Node} this This node
34762         * @param {Boolean} deep
34763         * @param {Boolean} anim
34764         */
34765         "beforecollapse" : true,
34766         /**
34767         * @event expand
34768         * Fires when this node is expanded
34769         * @param {Node} this This node
34770         */
34771         "expand" : true,
34772         /**
34773         * @event disabledchange
34774         * Fires when the disabled status of this node changes
34775         * @param {Node} this This node
34776         * @param {Boolean} disabled
34777         */
34778         "disabledchange" : true,
34779         /**
34780         * @event collapse
34781         * Fires when this node is collapsed
34782         * @param {Node} this This node
34783         */
34784         "collapse" : true,
34785         /**
34786         * @event beforeclick
34787         * Fires before click processing. Return false to cancel the default action.
34788         * @param {Node} this This node
34789         * @param {Roo.EventObject} e The event object
34790         */
34791         "beforeclick":true,
34792         /**
34793         * @event checkchange
34794         * Fires when a node with a checkbox's checked property changes
34795         * @param {Node} this This node
34796         * @param {Boolean} checked
34797         */
34798         "checkchange":true,
34799         /**
34800         * @event click
34801         * Fires when this node is clicked
34802         * @param {Node} this This node
34803         * @param {Roo.EventObject} e The event object
34804         */
34805         "click":true,
34806         /**
34807         * @event dblclick
34808         * Fires when this node is double clicked
34809         * @param {Node} this This node
34810         * @param {Roo.EventObject} e The event object
34811         */
34812         "dblclick":true,
34813         /**
34814         * @event contextmenu
34815         * Fires when this node is right clicked
34816         * @param {Node} this This node
34817         * @param {Roo.EventObject} e The event object
34818         */
34819         "contextmenu":true,
34820         /**
34821         * @event beforechildrenrendered
34822         * Fires right before the child nodes for this node are rendered
34823         * @param {Node} this This node
34824         */
34825         "beforechildrenrendered":true
34826     });
34827
34828     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34829
34830     /**
34831      * Read-only. The UI for this node
34832      * @type TreeNodeUI
34833      */
34834     this.ui = new uiClass(this);
34835     
34836     // finally support items[]
34837     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34838         return;
34839     }
34840     
34841     
34842     Roo.each(this.attributes.items, function(c) {
34843         this.appendChild(Roo.factory(c,Roo.Tree));
34844     }, this);
34845     delete this.attributes.items;
34846     
34847     
34848     
34849 };
34850 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34851     preventHScroll: true,
34852     /**
34853      * Returns true if this node is expanded
34854      * @return {Boolean}
34855      */
34856     isExpanded : function(){
34857         return this.expanded;
34858     },
34859
34860     /**
34861      * Returns the UI object for this node
34862      * @return {TreeNodeUI}
34863      */
34864     getUI : function(){
34865         return this.ui;
34866     },
34867
34868     // private override
34869     setFirstChild : function(node){
34870         var of = this.firstChild;
34871         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34872         if(this.childrenRendered && of && node != of){
34873             of.renderIndent(true, true);
34874         }
34875         if(this.rendered){
34876             this.renderIndent(true, true);
34877         }
34878     },
34879
34880     // private override
34881     setLastChild : function(node){
34882         var ol = this.lastChild;
34883         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34884         if(this.childrenRendered && ol && node != ol){
34885             ol.renderIndent(true, true);
34886         }
34887         if(this.rendered){
34888             this.renderIndent(true, true);
34889         }
34890     },
34891
34892     // these methods are overridden to provide lazy rendering support
34893     // private override
34894     appendChild : function()
34895     {
34896         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34897         if(node && this.childrenRendered){
34898             node.render();
34899         }
34900         this.ui.updateExpandIcon();
34901         return node;
34902     },
34903
34904     // private override
34905     removeChild : function(node){
34906         this.ownerTree.getSelectionModel().unselect(node);
34907         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34908         // if it's been rendered remove dom node
34909         if(this.childrenRendered){
34910             node.ui.remove();
34911         }
34912         if(this.childNodes.length < 1){
34913             this.collapse(false, false);
34914         }else{
34915             this.ui.updateExpandIcon();
34916         }
34917         if(!this.firstChild) {
34918             this.childrenRendered = false;
34919         }
34920         return node;
34921     },
34922
34923     // private override
34924     insertBefore : function(node, refNode){
34925         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34926         if(newNode && refNode && this.childrenRendered){
34927             node.render();
34928         }
34929         this.ui.updateExpandIcon();
34930         return newNode;
34931     },
34932
34933     /**
34934      * Sets the text for this node
34935      * @param {String} text
34936      */
34937     setText : function(text){
34938         var oldText = this.text;
34939         this.text = text;
34940         this.attributes.text = text;
34941         if(this.rendered){ // event without subscribing
34942             this.ui.onTextChange(this, text, oldText);
34943         }
34944         this.fireEvent("textchange", this, text, oldText);
34945     },
34946
34947     /**
34948      * Triggers selection of this node
34949      */
34950     select : function(){
34951         this.getOwnerTree().getSelectionModel().select(this);
34952     },
34953
34954     /**
34955      * Triggers deselection of this node
34956      */
34957     unselect : function(){
34958         this.getOwnerTree().getSelectionModel().unselect(this);
34959     },
34960
34961     /**
34962      * Returns true if this node is selected
34963      * @return {Boolean}
34964      */
34965     isSelected : function(){
34966         return this.getOwnerTree().getSelectionModel().isSelected(this);
34967     },
34968
34969     /**
34970      * Expand this node.
34971      * @param {Boolean} deep (optional) True to expand all children as well
34972      * @param {Boolean} anim (optional) false to cancel the default animation
34973      * @param {Function} callback (optional) A callback to be called when
34974      * expanding this node completes (does not wait for deep expand to complete).
34975      * Called with 1 parameter, this node.
34976      */
34977     expand : function(deep, anim, callback){
34978         if(!this.expanded){
34979             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34980                 return;
34981             }
34982             if(!this.childrenRendered){
34983                 this.renderChildren();
34984             }
34985             this.expanded = true;
34986             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34987                 this.ui.animExpand(function(){
34988                     this.fireEvent("expand", this);
34989                     if(typeof callback == "function"){
34990                         callback(this);
34991                     }
34992                     if(deep === true){
34993                         this.expandChildNodes(true);
34994                     }
34995                 }.createDelegate(this));
34996                 return;
34997             }else{
34998                 this.ui.expand();
34999                 this.fireEvent("expand", this);
35000                 if(typeof callback == "function"){
35001                     callback(this);
35002                 }
35003             }
35004         }else{
35005            if(typeof callback == "function"){
35006                callback(this);
35007            }
35008         }
35009         if(deep === true){
35010             this.expandChildNodes(true);
35011         }
35012     },
35013
35014     isHiddenRoot : function(){
35015         return this.isRoot && !this.getOwnerTree().rootVisible;
35016     },
35017
35018     /**
35019      * Collapse this node.
35020      * @param {Boolean} deep (optional) True to collapse all children as well
35021      * @param {Boolean} anim (optional) false to cancel the default animation
35022      */
35023     collapse : function(deep, anim){
35024         if(this.expanded && !this.isHiddenRoot()){
35025             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35026                 return;
35027             }
35028             this.expanded = false;
35029             if((this.getOwnerTree().animate && anim !== false) || anim){
35030                 this.ui.animCollapse(function(){
35031                     this.fireEvent("collapse", this);
35032                     if(deep === true){
35033                         this.collapseChildNodes(true);
35034                     }
35035                 }.createDelegate(this));
35036                 return;
35037             }else{
35038                 this.ui.collapse();
35039                 this.fireEvent("collapse", this);
35040             }
35041         }
35042         if(deep === true){
35043             var cs = this.childNodes;
35044             for(var i = 0, len = cs.length; i < len; i++) {
35045                 cs[i].collapse(true, false);
35046             }
35047         }
35048     },
35049
35050     // private
35051     delayedExpand : function(delay){
35052         if(!this.expandProcId){
35053             this.expandProcId = this.expand.defer(delay, this);
35054         }
35055     },
35056
35057     // private
35058     cancelExpand : function(){
35059         if(this.expandProcId){
35060             clearTimeout(this.expandProcId);
35061         }
35062         this.expandProcId = false;
35063     },
35064
35065     /**
35066      * Toggles expanded/collapsed state of the node
35067      */
35068     toggle : function(){
35069         if(this.expanded){
35070             this.collapse();
35071         }else{
35072             this.expand();
35073         }
35074     },
35075
35076     /**
35077      * Ensures all parent nodes are expanded
35078      */
35079     ensureVisible : function(callback){
35080         var tree = this.getOwnerTree();
35081         tree.expandPath(this.parentNode.getPath(), false, function(){
35082             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35083             Roo.callback(callback);
35084         }.createDelegate(this));
35085     },
35086
35087     /**
35088      * Expand all child nodes
35089      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35090      */
35091     expandChildNodes : function(deep){
35092         var cs = this.childNodes;
35093         for(var i = 0, len = cs.length; i < len; i++) {
35094                 cs[i].expand(deep);
35095         }
35096     },
35097
35098     /**
35099      * Collapse all child nodes
35100      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35101      */
35102     collapseChildNodes : function(deep){
35103         var cs = this.childNodes;
35104         for(var i = 0, len = cs.length; i < len; i++) {
35105                 cs[i].collapse(deep);
35106         }
35107     },
35108
35109     /**
35110      * Disables this node
35111      */
35112     disable : function(){
35113         this.disabled = true;
35114         this.unselect();
35115         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35116             this.ui.onDisableChange(this, true);
35117         }
35118         this.fireEvent("disabledchange", this, true);
35119     },
35120
35121     /**
35122      * Enables this node
35123      */
35124     enable : function(){
35125         this.disabled = false;
35126         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35127             this.ui.onDisableChange(this, false);
35128         }
35129         this.fireEvent("disabledchange", this, false);
35130     },
35131
35132     // private
35133     renderChildren : function(suppressEvent){
35134         if(suppressEvent !== false){
35135             this.fireEvent("beforechildrenrendered", this);
35136         }
35137         var cs = this.childNodes;
35138         for(var i = 0, len = cs.length; i < len; i++){
35139             cs[i].render(true);
35140         }
35141         this.childrenRendered = true;
35142     },
35143
35144     // private
35145     sort : function(fn, scope){
35146         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35147         if(this.childrenRendered){
35148             var cs = this.childNodes;
35149             for(var i = 0, len = cs.length; i < len; i++){
35150                 cs[i].render(true);
35151             }
35152         }
35153     },
35154
35155     // private
35156     render : function(bulkRender){
35157         this.ui.render(bulkRender);
35158         if(!this.rendered){
35159             this.rendered = true;
35160             if(this.expanded){
35161                 this.expanded = false;
35162                 this.expand(false, false);
35163             }
35164         }
35165     },
35166
35167     // private
35168     renderIndent : function(deep, refresh){
35169         if(refresh){
35170             this.ui.childIndent = null;
35171         }
35172         this.ui.renderIndent();
35173         if(deep === true && this.childrenRendered){
35174             var cs = this.childNodes;
35175             for(var i = 0, len = cs.length; i < len; i++){
35176                 cs[i].renderIndent(true, refresh);
35177             }
35178         }
35179     }
35180 });/*
35181  * Based on:
35182  * Ext JS Library 1.1.1
35183  * Copyright(c) 2006-2007, Ext JS, LLC.
35184  *
35185  * Originally Released Under LGPL - original licence link has changed is not relivant.
35186  *
35187  * Fork - LGPL
35188  * <script type="text/javascript">
35189  */
35190  
35191 /**
35192  * @class Roo.tree.AsyncTreeNode
35193  * @extends Roo.tree.TreeNode
35194  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35195  * @constructor
35196  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35197  */
35198  Roo.tree.AsyncTreeNode = function(config){
35199     this.loaded = false;
35200     this.loading = false;
35201     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35202     /**
35203     * @event beforeload
35204     * Fires before this node is loaded, return false to cancel
35205     * @param {Node} this This node
35206     */
35207     this.addEvents({'beforeload':true, 'load': true});
35208     /**
35209     * @event load
35210     * Fires when this node is loaded
35211     * @param {Node} this This node
35212     */
35213     /**
35214      * The loader used by this node (defaults to using the tree's defined loader)
35215      * @type TreeLoader
35216      * @property loader
35217      */
35218 };
35219 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35220     expand : function(deep, anim, callback){
35221         if(this.loading){ // if an async load is already running, waiting til it's done
35222             var timer;
35223             var f = function(){
35224                 if(!this.loading){ // done loading
35225                     clearInterval(timer);
35226                     this.expand(deep, anim, callback);
35227                 }
35228             }.createDelegate(this);
35229             timer = setInterval(f, 200);
35230             return;
35231         }
35232         if(!this.loaded){
35233             if(this.fireEvent("beforeload", this) === false){
35234                 return;
35235             }
35236             this.loading = true;
35237             this.ui.beforeLoad(this);
35238             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35239             if(loader){
35240                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35241                 return;
35242             }
35243         }
35244         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35245     },
35246     
35247     /**
35248      * Returns true if this node is currently loading
35249      * @return {Boolean}
35250      */
35251     isLoading : function(){
35252         return this.loading;  
35253     },
35254     
35255     loadComplete : function(deep, anim, callback){
35256         this.loading = false;
35257         this.loaded = true;
35258         this.ui.afterLoad(this);
35259         this.fireEvent("load", this);
35260         this.expand(deep, anim, callback);
35261     },
35262     
35263     /**
35264      * Returns true if this node has been loaded
35265      * @return {Boolean}
35266      */
35267     isLoaded : function(){
35268         return this.loaded;
35269     },
35270     
35271     hasChildNodes : function(){
35272         if(!this.isLeaf() && !this.loaded){
35273             return true;
35274         }else{
35275             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35276         }
35277     },
35278
35279     /**
35280      * Trigger a reload for this node
35281      * @param {Function} callback
35282      */
35283     reload : function(callback){
35284         this.collapse(false, false);
35285         while(this.firstChild){
35286             this.removeChild(this.firstChild);
35287         }
35288         this.childrenRendered = false;
35289         this.loaded = false;
35290         if(this.isHiddenRoot()){
35291             this.expanded = false;
35292         }
35293         this.expand(false, false, callback);
35294     }
35295 });/*
35296  * Based on:
35297  * Ext JS Library 1.1.1
35298  * Copyright(c) 2006-2007, Ext JS, LLC.
35299  *
35300  * Originally Released Under LGPL - original licence link has changed is not relivant.
35301  *
35302  * Fork - LGPL
35303  * <script type="text/javascript">
35304  */
35305  
35306 /**
35307  * @class Roo.tree.TreeNodeUI
35308  * @constructor
35309  * @param {Object} node The node to render
35310  * The TreeNode UI implementation is separate from the
35311  * tree implementation. Unless you are customizing the tree UI,
35312  * you should never have to use this directly.
35313  */
35314 Roo.tree.TreeNodeUI = function(node){
35315     this.node = node;
35316     this.rendered = false;
35317     this.animating = false;
35318     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35319 };
35320
35321 Roo.tree.TreeNodeUI.prototype = {
35322     removeChild : function(node){
35323         if(this.rendered){
35324             this.ctNode.removeChild(node.ui.getEl());
35325         }
35326     },
35327
35328     beforeLoad : function(){
35329          this.addClass("x-tree-node-loading");
35330     },
35331
35332     afterLoad : function(){
35333          this.removeClass("x-tree-node-loading");
35334     },
35335
35336     onTextChange : function(node, text, oldText){
35337         if(this.rendered){
35338             this.textNode.innerHTML = text;
35339         }
35340     },
35341
35342     onDisableChange : function(node, state){
35343         this.disabled = state;
35344         if(state){
35345             this.addClass("x-tree-node-disabled");
35346         }else{
35347             this.removeClass("x-tree-node-disabled");
35348         }
35349     },
35350
35351     onSelectedChange : function(state){
35352         if(state){
35353             this.focus();
35354             this.addClass("x-tree-selected");
35355         }else{
35356             //this.blur();
35357             this.removeClass("x-tree-selected");
35358         }
35359     },
35360
35361     onMove : function(tree, node, oldParent, newParent, index, refNode){
35362         this.childIndent = null;
35363         if(this.rendered){
35364             var targetNode = newParent.ui.getContainer();
35365             if(!targetNode){//target not rendered
35366                 this.holder = document.createElement("div");
35367                 this.holder.appendChild(this.wrap);
35368                 return;
35369             }
35370             var insertBefore = refNode ? refNode.ui.getEl() : null;
35371             if(insertBefore){
35372                 targetNode.insertBefore(this.wrap, insertBefore);
35373             }else{
35374                 targetNode.appendChild(this.wrap);
35375             }
35376             this.node.renderIndent(true);
35377         }
35378     },
35379
35380     addClass : function(cls){
35381         if(this.elNode){
35382             Roo.fly(this.elNode).addClass(cls);
35383         }
35384     },
35385
35386     removeClass : function(cls){
35387         if(this.elNode){
35388             Roo.fly(this.elNode).removeClass(cls);
35389         }
35390     },
35391
35392     remove : function(){
35393         if(this.rendered){
35394             this.holder = document.createElement("div");
35395             this.holder.appendChild(this.wrap);
35396         }
35397     },
35398
35399     fireEvent : function(){
35400         return this.node.fireEvent.apply(this.node, arguments);
35401     },
35402
35403     initEvents : function(){
35404         this.node.on("move", this.onMove, this);
35405         var E = Roo.EventManager;
35406         var a = this.anchor;
35407
35408         var el = Roo.fly(a, '_treeui');
35409
35410         if(Roo.isOpera){ // opera render bug ignores the CSS
35411             el.setStyle("text-decoration", "none");
35412         }
35413
35414         el.on("click", this.onClick, this);
35415         el.on("dblclick", this.onDblClick, this);
35416
35417         if(this.checkbox){
35418             Roo.EventManager.on(this.checkbox,
35419                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35420         }
35421
35422         el.on("contextmenu", this.onContextMenu, this);
35423
35424         var icon = Roo.fly(this.iconNode);
35425         icon.on("click", this.onClick, this);
35426         icon.on("dblclick", this.onDblClick, this);
35427         icon.on("contextmenu", this.onContextMenu, this);
35428         E.on(this.ecNode, "click", this.ecClick, this, true);
35429
35430         if(this.node.disabled){
35431             this.addClass("x-tree-node-disabled");
35432         }
35433         if(this.node.hidden){
35434             this.addClass("x-tree-node-disabled");
35435         }
35436         var ot = this.node.getOwnerTree();
35437         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35438         if(dd && (!this.node.isRoot || ot.rootVisible)){
35439             Roo.dd.Registry.register(this.elNode, {
35440                 node: this.node,
35441                 handles: this.getDDHandles(),
35442                 isHandle: false
35443             });
35444         }
35445     },
35446
35447     getDDHandles : function(){
35448         return [this.iconNode, this.textNode];
35449     },
35450
35451     hide : function(){
35452         if(this.rendered){
35453             this.wrap.style.display = "none";
35454         }
35455     },
35456
35457     show : function(){
35458         if(this.rendered){
35459             this.wrap.style.display = "";
35460         }
35461     },
35462
35463     onContextMenu : function(e){
35464         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35465             e.preventDefault();
35466             this.focus();
35467             this.fireEvent("contextmenu", this.node, e);
35468         }
35469     },
35470
35471     onClick : function(e){
35472         if(this.dropping){
35473             e.stopEvent();
35474             return;
35475         }
35476         if(this.fireEvent("beforeclick", this.node, e) !== false){
35477             if(!this.disabled && this.node.attributes.href){
35478                 this.fireEvent("click", this.node, e);
35479                 return;
35480             }
35481             e.preventDefault();
35482             if(this.disabled){
35483                 return;
35484             }
35485
35486             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35487                 this.node.toggle();
35488             }
35489
35490             this.fireEvent("click", this.node, e);
35491         }else{
35492             e.stopEvent();
35493         }
35494     },
35495
35496     onDblClick : function(e){
35497         e.preventDefault();
35498         if(this.disabled){
35499             return;
35500         }
35501         if(this.checkbox){
35502             this.toggleCheck();
35503         }
35504         if(!this.animating && this.node.hasChildNodes()){
35505             this.node.toggle();
35506         }
35507         this.fireEvent("dblclick", this.node, e);
35508     },
35509
35510     onCheckChange : function(){
35511         var checked = this.checkbox.checked;
35512         this.node.attributes.checked = checked;
35513         this.fireEvent('checkchange', this.node, checked);
35514     },
35515
35516     ecClick : function(e){
35517         if(!this.animating && this.node.hasChildNodes()){
35518             this.node.toggle();
35519         }
35520     },
35521
35522     startDrop : function(){
35523         this.dropping = true;
35524     },
35525
35526     // delayed drop so the click event doesn't get fired on a drop
35527     endDrop : function(){
35528        setTimeout(function(){
35529            this.dropping = false;
35530        }.createDelegate(this), 50);
35531     },
35532
35533     expand : function(){
35534         this.updateExpandIcon();
35535         this.ctNode.style.display = "";
35536     },
35537
35538     focus : function(){
35539         if(!this.node.preventHScroll){
35540             try{this.anchor.focus();
35541             }catch(e){}
35542         }else if(!Roo.isIE){
35543             try{
35544                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35545                 var l = noscroll.scrollLeft;
35546                 this.anchor.focus();
35547                 noscroll.scrollLeft = l;
35548             }catch(e){}
35549         }
35550     },
35551
35552     toggleCheck : function(value){
35553         var cb = this.checkbox;
35554         if(cb){
35555             cb.checked = (value === undefined ? !cb.checked : value);
35556         }
35557     },
35558
35559     blur : function(){
35560         try{
35561             this.anchor.blur();
35562         }catch(e){}
35563     },
35564
35565     animExpand : function(callback){
35566         var ct = Roo.get(this.ctNode);
35567         ct.stopFx();
35568         if(!this.node.hasChildNodes()){
35569             this.updateExpandIcon();
35570             this.ctNode.style.display = "";
35571             Roo.callback(callback);
35572             return;
35573         }
35574         this.animating = true;
35575         this.updateExpandIcon();
35576
35577         ct.slideIn('t', {
35578            callback : function(){
35579                this.animating = false;
35580                Roo.callback(callback);
35581             },
35582             scope: this,
35583             duration: this.node.ownerTree.duration || .25
35584         });
35585     },
35586
35587     highlight : function(){
35588         var tree = this.node.getOwnerTree();
35589         Roo.fly(this.wrap).highlight(
35590             tree.hlColor || "C3DAF9",
35591             {endColor: tree.hlBaseColor}
35592         );
35593     },
35594
35595     collapse : function(){
35596         this.updateExpandIcon();
35597         this.ctNode.style.display = "none";
35598     },
35599
35600     animCollapse : function(callback){
35601         var ct = Roo.get(this.ctNode);
35602         ct.enableDisplayMode('block');
35603         ct.stopFx();
35604
35605         this.animating = true;
35606         this.updateExpandIcon();
35607
35608         ct.slideOut('t', {
35609             callback : function(){
35610                this.animating = false;
35611                Roo.callback(callback);
35612             },
35613             scope: this,
35614             duration: this.node.ownerTree.duration || .25
35615         });
35616     },
35617
35618     getContainer : function(){
35619         return this.ctNode;
35620     },
35621
35622     getEl : function(){
35623         return this.wrap;
35624     },
35625
35626     appendDDGhost : function(ghostNode){
35627         ghostNode.appendChild(this.elNode.cloneNode(true));
35628     },
35629
35630     getDDRepairXY : function(){
35631         return Roo.lib.Dom.getXY(this.iconNode);
35632     },
35633
35634     onRender : function(){
35635         this.render();
35636     },
35637
35638     render : function(bulkRender){
35639         var n = this.node, a = n.attributes;
35640         var targetNode = n.parentNode ?
35641               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35642
35643         if(!this.rendered){
35644             this.rendered = true;
35645
35646             this.renderElements(n, a, targetNode, bulkRender);
35647
35648             if(a.qtip){
35649                if(this.textNode.setAttributeNS){
35650                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35651                    if(a.qtipTitle){
35652                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35653                    }
35654                }else{
35655                    this.textNode.setAttribute("ext:qtip", a.qtip);
35656                    if(a.qtipTitle){
35657                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35658                    }
35659                }
35660             }else if(a.qtipCfg){
35661                 a.qtipCfg.target = Roo.id(this.textNode);
35662                 Roo.QuickTips.register(a.qtipCfg);
35663             }
35664             this.initEvents();
35665             if(!this.node.expanded){
35666                 this.updateExpandIcon();
35667             }
35668         }else{
35669             if(bulkRender === true) {
35670                 targetNode.appendChild(this.wrap);
35671             }
35672         }
35673     },
35674
35675     renderElements : function(n, a, targetNode, bulkRender)
35676     {
35677         // add some indent caching, this helps performance when rendering a large tree
35678         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35679         var t = n.getOwnerTree();
35680         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35681         if (typeof(n.attributes.html) != 'undefined') {
35682             txt = n.attributes.html;
35683         }
35684         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35685         var cb = typeof a.checked == 'boolean';
35686         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35687         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35688             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35689             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35690             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35691             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35692             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35693              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35694                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35695             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35696             "</li>"];
35697
35698         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35699             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35700                                 n.nextSibling.ui.getEl(), buf.join(""));
35701         }else{
35702             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35703         }
35704
35705         this.elNode = this.wrap.childNodes[0];
35706         this.ctNode = this.wrap.childNodes[1];
35707         var cs = this.elNode.childNodes;
35708         this.indentNode = cs[0];
35709         this.ecNode = cs[1];
35710         this.iconNode = cs[2];
35711         var index = 3;
35712         if(cb){
35713             this.checkbox = cs[3];
35714             index++;
35715         }
35716         this.anchor = cs[index];
35717         this.textNode = cs[index].firstChild;
35718     },
35719
35720     getAnchor : function(){
35721         return this.anchor;
35722     },
35723
35724     getTextEl : function(){
35725         return this.textNode;
35726     },
35727
35728     getIconEl : function(){
35729         return this.iconNode;
35730     },
35731
35732     isChecked : function(){
35733         return this.checkbox ? this.checkbox.checked : false;
35734     },
35735
35736     updateExpandIcon : function(){
35737         if(this.rendered){
35738             var n = this.node, c1, c2;
35739             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35740             var hasChild = n.hasChildNodes();
35741             if(hasChild){
35742                 if(n.expanded){
35743                     cls += "-minus";
35744                     c1 = "x-tree-node-collapsed";
35745                     c2 = "x-tree-node-expanded";
35746                 }else{
35747                     cls += "-plus";
35748                     c1 = "x-tree-node-expanded";
35749                     c2 = "x-tree-node-collapsed";
35750                 }
35751                 if(this.wasLeaf){
35752                     this.removeClass("x-tree-node-leaf");
35753                     this.wasLeaf = false;
35754                 }
35755                 if(this.c1 != c1 || this.c2 != c2){
35756                     Roo.fly(this.elNode).replaceClass(c1, c2);
35757                     this.c1 = c1; this.c2 = c2;
35758                 }
35759             }else{
35760                 // this changes non-leafs into leafs if they have no children.
35761                 // it's not very rational behaviour..
35762                 
35763                 if(!this.wasLeaf && this.node.leaf){
35764                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35765                     delete this.c1;
35766                     delete this.c2;
35767                     this.wasLeaf = true;
35768                 }
35769             }
35770             var ecc = "x-tree-ec-icon "+cls;
35771             if(this.ecc != ecc){
35772                 this.ecNode.className = ecc;
35773                 this.ecc = ecc;
35774             }
35775         }
35776     },
35777
35778     getChildIndent : function(){
35779         if(!this.childIndent){
35780             var buf = [];
35781             var p = this.node;
35782             while(p){
35783                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35784                     if(!p.isLast()) {
35785                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35786                     } else {
35787                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35788                     }
35789                 }
35790                 p = p.parentNode;
35791             }
35792             this.childIndent = buf.join("");
35793         }
35794         return this.childIndent;
35795     },
35796
35797     renderIndent : function(){
35798         if(this.rendered){
35799             var indent = "";
35800             var p = this.node.parentNode;
35801             if(p){
35802                 indent = p.ui.getChildIndent();
35803             }
35804             if(this.indentMarkup != indent){ // don't rerender if not required
35805                 this.indentNode.innerHTML = indent;
35806                 this.indentMarkup = indent;
35807             }
35808             this.updateExpandIcon();
35809         }
35810     }
35811 };
35812
35813 Roo.tree.RootTreeNodeUI = function(){
35814     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35815 };
35816 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35817     render : function(){
35818         if(!this.rendered){
35819             var targetNode = this.node.ownerTree.innerCt.dom;
35820             this.node.expanded = true;
35821             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35822             this.wrap = this.ctNode = targetNode.firstChild;
35823         }
35824     },
35825     collapse : function(){
35826     },
35827     expand : function(){
35828     }
35829 });/*
35830  * Based on:
35831  * Ext JS Library 1.1.1
35832  * Copyright(c) 2006-2007, Ext JS, LLC.
35833  *
35834  * Originally Released Under LGPL - original licence link has changed is not relivant.
35835  *
35836  * Fork - LGPL
35837  * <script type="text/javascript">
35838  */
35839 /**
35840  * @class Roo.tree.TreeLoader
35841  * @extends Roo.util.Observable
35842  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35843  * nodes from a specified URL. The response must be a javascript Array definition
35844  * who's elements are node definition objects. eg:
35845  * <pre><code>
35846 {  success : true,
35847    data :      [
35848    
35849     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35850     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35851     ]
35852 }
35853
35854
35855 </code></pre>
35856  * <br><br>
35857  * The old style respose with just an array is still supported, but not recommended.
35858  * <br><br>
35859  *
35860  * A server request is sent, and child nodes are loaded only when a node is expanded.
35861  * The loading node's id is passed to the server under the parameter name "node" to
35862  * enable the server to produce the correct child nodes.
35863  * <br><br>
35864  * To pass extra parameters, an event handler may be attached to the "beforeload"
35865  * event, and the parameters specified in the TreeLoader's baseParams property:
35866  * <pre><code>
35867     myTreeLoader.on("beforeload", function(treeLoader, node) {
35868         this.baseParams.category = node.attributes.category;
35869     }, this);
35870 </code></pre><
35871  * This would pass an HTTP parameter called "category" to the server containing
35872  * the value of the Node's "category" attribute.
35873  * @constructor
35874  * Creates a new Treeloader.
35875  * @param {Object} config A config object containing config properties.
35876  */
35877 Roo.tree.TreeLoader = function(config){
35878     this.baseParams = {};
35879     this.requestMethod = "POST";
35880     Roo.apply(this, config);
35881
35882     this.addEvents({
35883     
35884         /**
35885          * @event beforeload
35886          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35887          * @param {Object} This TreeLoader object.
35888          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35889          * @param {Object} callback The callback function specified in the {@link #load} call.
35890          */
35891         beforeload : true,
35892         /**
35893          * @event load
35894          * Fires when the node has been successfuly loaded.
35895          * @param {Object} This TreeLoader object.
35896          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35897          * @param {Object} response The response object containing the data from the server.
35898          */
35899         load : true,
35900         /**
35901          * @event loadexception
35902          * Fires if the network request failed.
35903          * @param {Object} This TreeLoader object.
35904          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35905          * @param {Object} response The response object containing the data from the server.
35906          */
35907         loadexception : true,
35908         /**
35909          * @event create
35910          * Fires before a node is created, enabling you to return custom Node types 
35911          * @param {Object} This TreeLoader object.
35912          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35913          */
35914         create : true
35915     });
35916
35917     Roo.tree.TreeLoader.superclass.constructor.call(this);
35918 };
35919
35920 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35921     /**
35922     * @cfg {String} dataUrl The URL from which to request a Json string which
35923     * specifies an array of node definition object representing the child nodes
35924     * to be loaded.
35925     */
35926     /**
35927     * @cfg {String} requestMethod either GET or POST
35928     * defaults to POST (due to BC)
35929     * to be loaded.
35930     */
35931     /**
35932     * @cfg {Object} baseParams (optional) An object containing properties which
35933     * specify HTTP parameters to be passed to each request for child nodes.
35934     */
35935     /**
35936     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35937     * created by this loader. If the attributes sent by the server have an attribute in this object,
35938     * they take priority.
35939     */
35940     /**
35941     * @cfg {Object} uiProviders (optional) An object containing properties which
35942     * 
35943     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35944     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35945     * <i>uiProvider</i> attribute of a returned child node is a string rather
35946     * than a reference to a TreeNodeUI implementation, this that string value
35947     * is used as a property name in the uiProviders object. You can define the provider named
35948     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35949     */
35950     uiProviders : {},
35951
35952     /**
35953     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35954     * child nodes before loading.
35955     */
35956     clearOnLoad : true,
35957
35958     /**
35959     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35960     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35961     * Grid query { data : [ .....] }
35962     */
35963     
35964     root : false,
35965      /**
35966     * @cfg {String} queryParam (optional) 
35967     * Name of the query as it will be passed on the querystring (defaults to 'node')
35968     * eg. the request will be ?node=[id]
35969     */
35970     
35971     
35972     queryParam: false,
35973     
35974     /**
35975      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35976      * This is called automatically when a node is expanded, but may be used to reload
35977      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35978      * @param {Roo.tree.TreeNode} node
35979      * @param {Function} callback
35980      */
35981     load : function(node, callback){
35982         if(this.clearOnLoad){
35983             while(node.firstChild){
35984                 node.removeChild(node.firstChild);
35985             }
35986         }
35987         if(node.attributes.children){ // preloaded json children
35988             var cs = node.attributes.children;
35989             for(var i = 0, len = cs.length; i < len; i++){
35990                 node.appendChild(this.createNode(cs[i]));
35991             }
35992             if(typeof callback == "function"){
35993                 callback();
35994             }
35995         }else if(this.dataUrl){
35996             this.requestData(node, callback);
35997         }
35998     },
35999
36000     getParams: function(node){
36001         var buf = [], bp = this.baseParams;
36002         for(var key in bp){
36003             if(typeof bp[key] != "function"){
36004                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36005             }
36006         }
36007         var n = this.queryParam === false ? 'node' : this.queryParam;
36008         buf.push(n + "=", encodeURIComponent(node.id));
36009         return buf.join("");
36010     },
36011
36012     requestData : function(node, callback){
36013         if(this.fireEvent("beforeload", this, node, callback) !== false){
36014             this.transId = Roo.Ajax.request({
36015                 method:this.requestMethod,
36016                 url: this.dataUrl||this.url,
36017                 success: this.handleResponse,
36018                 failure: this.handleFailure,
36019                 scope: this,
36020                 argument: {callback: callback, node: node},
36021                 params: this.getParams(node)
36022             });
36023         }else{
36024             // if the load is cancelled, make sure we notify
36025             // the node that we are done
36026             if(typeof callback == "function"){
36027                 callback();
36028             }
36029         }
36030     },
36031
36032     isLoading : function(){
36033         return this.transId ? true : false;
36034     },
36035
36036     abort : function(){
36037         if(this.isLoading()){
36038             Roo.Ajax.abort(this.transId);
36039         }
36040     },
36041
36042     // private
36043     createNode : function(attr)
36044     {
36045         // apply baseAttrs, nice idea Corey!
36046         if(this.baseAttrs){
36047             Roo.applyIf(attr, this.baseAttrs);
36048         }
36049         if(this.applyLoader !== false){
36050             attr.loader = this;
36051         }
36052         // uiProvider = depreciated..
36053         
36054         if(typeof(attr.uiProvider) == 'string'){
36055            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36056                 /**  eval:var:attr */ eval(attr.uiProvider);
36057         }
36058         if(typeof(this.uiProviders['default']) != 'undefined') {
36059             attr.uiProvider = this.uiProviders['default'];
36060         }
36061         
36062         this.fireEvent('create', this, attr);
36063         
36064         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36065         return(attr.leaf ?
36066                         new Roo.tree.TreeNode(attr) :
36067                         new Roo.tree.AsyncTreeNode(attr));
36068     },
36069
36070     processResponse : function(response, node, callback)
36071     {
36072         var json = response.responseText;
36073         try {
36074             
36075             var o = Roo.decode(json);
36076             
36077             if (this.root === false && typeof(o.success) != undefined) {
36078                 this.root = 'data'; // the default behaviour for list like data..
36079                 }
36080                 
36081             if (this.root !== false &&  !o.success) {
36082                 // it's a failure condition.
36083                 var a = response.argument;
36084                 this.fireEvent("loadexception", this, a.node, response);
36085                 Roo.log("Load failed - should have a handler really");
36086                 return;
36087             }
36088             
36089             
36090             
36091             if (this.root !== false) {
36092                  o = o[this.root];
36093             }
36094             
36095             for(var i = 0, len = o.length; i < len; i++){
36096                 var n = this.createNode(o[i]);
36097                 if(n){
36098                     node.appendChild(n);
36099                 }
36100             }
36101             if(typeof callback == "function"){
36102                 callback(this, node);
36103             }
36104         }catch(e){
36105             this.handleFailure(response);
36106         }
36107     },
36108
36109     handleResponse : function(response){
36110         this.transId = false;
36111         var a = response.argument;
36112         this.processResponse(response, a.node, a.callback);
36113         this.fireEvent("load", this, a.node, response);
36114     },
36115
36116     handleFailure : function(response)
36117     {
36118         // should handle failure better..
36119         this.transId = false;
36120         var a = response.argument;
36121         this.fireEvent("loadexception", this, a.node, response);
36122         if(typeof a.callback == "function"){
36123             a.callback(this, a.node);
36124         }
36125     }
36126 });/*
36127  * Based on:
36128  * Ext JS Library 1.1.1
36129  * Copyright(c) 2006-2007, Ext JS, LLC.
36130  *
36131  * Originally Released Under LGPL - original licence link has changed is not relivant.
36132  *
36133  * Fork - LGPL
36134  * <script type="text/javascript">
36135  */
36136
36137 /**
36138 * @class Roo.tree.TreeFilter
36139 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36140 * @param {TreePanel} tree
36141 * @param {Object} config (optional)
36142  */
36143 Roo.tree.TreeFilter = function(tree, config){
36144     this.tree = tree;
36145     this.filtered = {};
36146     Roo.apply(this, config);
36147 };
36148
36149 Roo.tree.TreeFilter.prototype = {
36150     clearBlank:false,
36151     reverse:false,
36152     autoClear:false,
36153     remove:false,
36154
36155      /**
36156      * Filter the data by a specific attribute.
36157      * @param {String/RegExp} value Either string that the attribute value
36158      * should start with or a RegExp to test against the attribute
36159      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36160      * @param {TreeNode} startNode (optional) The node to start the filter at.
36161      */
36162     filter : function(value, attr, startNode){
36163         attr = attr || "text";
36164         var f;
36165         if(typeof value == "string"){
36166             var vlen = value.length;
36167             // auto clear empty filter
36168             if(vlen == 0 && this.clearBlank){
36169                 this.clear();
36170                 return;
36171             }
36172             value = value.toLowerCase();
36173             f = function(n){
36174                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36175             };
36176         }else if(value.exec){ // regex?
36177             f = function(n){
36178                 return value.test(n.attributes[attr]);
36179             };
36180         }else{
36181             throw 'Illegal filter type, must be string or regex';
36182         }
36183         this.filterBy(f, null, startNode);
36184         },
36185
36186     /**
36187      * Filter by a function. The passed function will be called with each
36188      * node in the tree (or from the startNode). If the function returns true, the node is kept
36189      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36190      * @param {Function} fn The filter function
36191      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36192      */
36193     filterBy : function(fn, scope, startNode){
36194         startNode = startNode || this.tree.root;
36195         if(this.autoClear){
36196             this.clear();
36197         }
36198         var af = this.filtered, rv = this.reverse;
36199         var f = function(n){
36200             if(n == startNode){
36201                 return true;
36202             }
36203             if(af[n.id]){
36204                 return false;
36205             }
36206             var m = fn.call(scope || n, n);
36207             if(!m || rv){
36208                 af[n.id] = n;
36209                 n.ui.hide();
36210                 return false;
36211             }
36212             return true;
36213         };
36214         startNode.cascade(f);
36215         if(this.remove){
36216            for(var id in af){
36217                if(typeof id != "function"){
36218                    var n = af[id];
36219                    if(n && n.parentNode){
36220                        n.parentNode.removeChild(n);
36221                    }
36222                }
36223            }
36224         }
36225     },
36226
36227     /**
36228      * Clears the current filter. Note: with the "remove" option
36229      * set a filter cannot be cleared.
36230      */
36231     clear : function(){
36232         var t = this.tree;
36233         var af = this.filtered;
36234         for(var id in af){
36235             if(typeof id != "function"){
36236                 var n = af[id];
36237                 if(n){
36238                     n.ui.show();
36239                 }
36240             }
36241         }
36242         this.filtered = {};
36243     }
36244 };
36245 /*
36246  * Based on:
36247  * Ext JS Library 1.1.1
36248  * Copyright(c) 2006-2007, Ext JS, LLC.
36249  *
36250  * Originally Released Under LGPL - original licence link has changed is not relivant.
36251  *
36252  * Fork - LGPL
36253  * <script type="text/javascript">
36254  */
36255  
36256
36257 /**
36258  * @class Roo.tree.TreeSorter
36259  * Provides sorting of nodes in a TreePanel
36260  * 
36261  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36262  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36263  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36264  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36265  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36266  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36267  * @constructor
36268  * @param {TreePanel} tree
36269  * @param {Object} config
36270  */
36271 Roo.tree.TreeSorter = function(tree, config){
36272     Roo.apply(this, config);
36273     tree.on("beforechildrenrendered", this.doSort, this);
36274     tree.on("append", this.updateSort, this);
36275     tree.on("insert", this.updateSort, this);
36276     
36277     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36278     var p = this.property || "text";
36279     var sortType = this.sortType;
36280     var fs = this.folderSort;
36281     var cs = this.caseSensitive === true;
36282     var leafAttr = this.leafAttr || 'leaf';
36283
36284     this.sortFn = function(n1, n2){
36285         if(fs){
36286             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36287                 return 1;
36288             }
36289             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36290                 return -1;
36291             }
36292         }
36293         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36294         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36295         if(v1 < v2){
36296                         return dsc ? +1 : -1;
36297                 }else if(v1 > v2){
36298                         return dsc ? -1 : +1;
36299         }else{
36300                 return 0;
36301         }
36302     };
36303 };
36304
36305 Roo.tree.TreeSorter.prototype = {
36306     doSort : function(node){
36307         node.sort(this.sortFn);
36308     },
36309     
36310     compareNodes : function(n1, n2){
36311         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36312     },
36313     
36314     updateSort : function(tree, node){
36315         if(node.childrenRendered){
36316             this.doSort.defer(1, this, [node]);
36317         }
36318     }
36319 };/*
36320  * Based on:
36321  * Ext JS Library 1.1.1
36322  * Copyright(c) 2006-2007, Ext JS, LLC.
36323  *
36324  * Originally Released Under LGPL - original licence link has changed is not relivant.
36325  *
36326  * Fork - LGPL
36327  * <script type="text/javascript">
36328  */
36329
36330 if(Roo.dd.DropZone){
36331     
36332 Roo.tree.TreeDropZone = function(tree, config){
36333     this.allowParentInsert = false;
36334     this.allowContainerDrop = false;
36335     this.appendOnly = false;
36336     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36337     this.tree = tree;
36338     this.lastInsertClass = "x-tree-no-status";
36339     this.dragOverData = {};
36340 };
36341
36342 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36343     ddGroup : "TreeDD",
36344     scroll:  true,
36345     
36346     expandDelay : 1000,
36347     
36348     expandNode : function(node){
36349         if(node.hasChildNodes() && !node.isExpanded()){
36350             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36351         }
36352     },
36353     
36354     queueExpand : function(node){
36355         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36356     },
36357     
36358     cancelExpand : function(){
36359         if(this.expandProcId){
36360             clearTimeout(this.expandProcId);
36361             this.expandProcId = false;
36362         }
36363     },
36364     
36365     isValidDropPoint : function(n, pt, dd, e, data){
36366         if(!n || !data){ return false; }
36367         var targetNode = n.node;
36368         var dropNode = data.node;
36369         // default drop rules
36370         if(!(targetNode && targetNode.isTarget && pt)){
36371             return false;
36372         }
36373         if(pt == "append" && targetNode.allowChildren === false){
36374             return false;
36375         }
36376         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36377             return false;
36378         }
36379         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36380             return false;
36381         }
36382         // reuse the object
36383         var overEvent = this.dragOverData;
36384         overEvent.tree = this.tree;
36385         overEvent.target = targetNode;
36386         overEvent.data = data;
36387         overEvent.point = pt;
36388         overEvent.source = dd;
36389         overEvent.rawEvent = e;
36390         overEvent.dropNode = dropNode;
36391         overEvent.cancel = false;  
36392         var result = this.tree.fireEvent("nodedragover", overEvent);
36393         return overEvent.cancel === false && result !== false;
36394     },
36395     
36396     getDropPoint : function(e, n, dd)
36397     {
36398         var tn = n.node;
36399         if(tn.isRoot){
36400             return tn.allowChildren !== false ? "append" : false; // always append for root
36401         }
36402         var dragEl = n.ddel;
36403         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36404         var y = Roo.lib.Event.getPageY(e);
36405         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36406         
36407         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36408         var noAppend = tn.allowChildren === false;
36409         if(this.appendOnly || tn.parentNode.allowChildren === false){
36410             return noAppend ? false : "append";
36411         }
36412         var noBelow = false;
36413         if(!this.allowParentInsert){
36414             noBelow = tn.hasChildNodes() && tn.isExpanded();
36415         }
36416         var q = (b - t) / (noAppend ? 2 : 3);
36417         if(y >= t && y < (t + q)){
36418             return "above";
36419         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36420             return "below";
36421         }else{
36422             return "append";
36423         }
36424     },
36425     
36426     onNodeEnter : function(n, dd, e, data)
36427     {
36428         this.cancelExpand();
36429     },
36430     
36431     onNodeOver : function(n, dd, e, data)
36432     {
36433        
36434         var pt = this.getDropPoint(e, n, dd);
36435         var node = n.node;
36436         
36437         // auto node expand check
36438         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36439             this.queueExpand(node);
36440         }else if(pt != "append"){
36441             this.cancelExpand();
36442         }
36443         
36444         // set the insert point style on the target node
36445         var returnCls = this.dropNotAllowed;
36446         if(this.isValidDropPoint(n, pt, dd, e, data)){
36447            if(pt){
36448                var el = n.ddel;
36449                var cls;
36450                if(pt == "above"){
36451                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36452                    cls = "x-tree-drag-insert-above";
36453                }else if(pt == "below"){
36454                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36455                    cls = "x-tree-drag-insert-below";
36456                }else{
36457                    returnCls = "x-tree-drop-ok-append";
36458                    cls = "x-tree-drag-append";
36459                }
36460                if(this.lastInsertClass != cls){
36461                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36462                    this.lastInsertClass = cls;
36463                }
36464            }
36465        }
36466        return returnCls;
36467     },
36468     
36469     onNodeOut : function(n, dd, e, data){
36470         
36471         this.cancelExpand();
36472         this.removeDropIndicators(n);
36473     },
36474     
36475     onNodeDrop : function(n, dd, e, data){
36476         var point = this.getDropPoint(e, n, dd);
36477         var targetNode = n.node;
36478         targetNode.ui.startDrop();
36479         if(!this.isValidDropPoint(n, point, dd, e, data)){
36480             targetNode.ui.endDrop();
36481             return false;
36482         }
36483         // first try to find the drop node
36484         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36485         var dropEvent = {
36486             tree : this.tree,
36487             target: targetNode,
36488             data: data,
36489             point: point,
36490             source: dd,
36491             rawEvent: e,
36492             dropNode: dropNode,
36493             cancel: !dropNode   
36494         };
36495         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36496         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36497             targetNode.ui.endDrop();
36498             return false;
36499         }
36500         // allow target changing
36501         targetNode = dropEvent.target;
36502         if(point == "append" && !targetNode.isExpanded()){
36503             targetNode.expand(false, null, function(){
36504                 this.completeDrop(dropEvent);
36505             }.createDelegate(this));
36506         }else{
36507             this.completeDrop(dropEvent);
36508         }
36509         return true;
36510     },
36511     
36512     completeDrop : function(de){
36513         var ns = de.dropNode, p = de.point, t = de.target;
36514         if(!(ns instanceof Array)){
36515             ns = [ns];
36516         }
36517         var n;
36518         for(var i = 0, len = ns.length; i < len; i++){
36519             n = ns[i];
36520             if(p == "above"){
36521                 t.parentNode.insertBefore(n, t);
36522             }else if(p == "below"){
36523                 t.parentNode.insertBefore(n, t.nextSibling);
36524             }else{
36525                 t.appendChild(n);
36526             }
36527         }
36528         n.ui.focus();
36529         if(this.tree.hlDrop){
36530             n.ui.highlight();
36531         }
36532         t.ui.endDrop();
36533         this.tree.fireEvent("nodedrop", de);
36534     },
36535     
36536     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36537         if(this.tree.hlDrop){
36538             dropNode.ui.focus();
36539             dropNode.ui.highlight();
36540         }
36541         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36542     },
36543     
36544     getTree : function(){
36545         return this.tree;
36546     },
36547     
36548     removeDropIndicators : function(n){
36549         if(n && n.ddel){
36550             var el = n.ddel;
36551             Roo.fly(el).removeClass([
36552                     "x-tree-drag-insert-above",
36553                     "x-tree-drag-insert-below",
36554                     "x-tree-drag-append"]);
36555             this.lastInsertClass = "_noclass";
36556         }
36557     },
36558     
36559     beforeDragDrop : function(target, e, id){
36560         this.cancelExpand();
36561         return true;
36562     },
36563     
36564     afterRepair : function(data){
36565         if(data && Roo.enableFx){
36566             data.node.ui.highlight();
36567         }
36568         this.hideProxy();
36569     } 
36570     
36571 });
36572
36573 }
36574 /*
36575  * Based on:
36576  * Ext JS Library 1.1.1
36577  * Copyright(c) 2006-2007, Ext JS, LLC.
36578  *
36579  * Originally Released Under LGPL - original licence link has changed is not relivant.
36580  *
36581  * Fork - LGPL
36582  * <script type="text/javascript">
36583  */
36584  
36585
36586 if(Roo.dd.DragZone){
36587 Roo.tree.TreeDragZone = function(tree, config){
36588     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36589     this.tree = tree;
36590 };
36591
36592 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36593     ddGroup : "TreeDD",
36594    
36595     onBeforeDrag : function(data, e){
36596         var n = data.node;
36597         return n && n.draggable && !n.disabled;
36598     },
36599      
36600     
36601     onInitDrag : function(e){
36602         var data = this.dragData;
36603         this.tree.getSelectionModel().select(data.node);
36604         this.proxy.update("");
36605         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36606         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36607     },
36608     
36609     getRepairXY : function(e, data){
36610         return data.node.ui.getDDRepairXY();
36611     },
36612     
36613     onEndDrag : function(data, e){
36614         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36615         
36616         
36617     },
36618     
36619     onValidDrop : function(dd, e, id){
36620         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36621         this.hideProxy();
36622     },
36623     
36624     beforeInvalidDrop : function(e, id){
36625         // this scrolls the original position back into view
36626         var sm = this.tree.getSelectionModel();
36627         sm.clearSelections();
36628         sm.select(this.dragData.node);
36629     }
36630 });
36631 }/*
36632  * Based on:
36633  * Ext JS Library 1.1.1
36634  * Copyright(c) 2006-2007, Ext JS, LLC.
36635  *
36636  * Originally Released Under LGPL - original licence link has changed is not relivant.
36637  *
36638  * Fork - LGPL
36639  * <script type="text/javascript">
36640  */
36641 /**
36642  * @class Roo.tree.TreeEditor
36643  * @extends Roo.Editor
36644  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36645  * as the editor field.
36646  * @constructor
36647  * @param {Object} config (used to be the tree panel.)
36648  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36649  * 
36650  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36651  * @cfg {Roo.form.TextField|Object} field The field configuration
36652  *
36653  * 
36654  */
36655 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36656     var tree = config;
36657     var field;
36658     if (oldconfig) { // old style..
36659         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36660     } else {
36661         // new style..
36662         tree = config.tree;
36663         config.field = config.field  || {};
36664         config.field.xtype = 'TextField';
36665         field = Roo.factory(config.field, Roo.form);
36666     }
36667     config = config || {};
36668     
36669     
36670     this.addEvents({
36671         /**
36672          * @event beforenodeedit
36673          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36674          * false from the handler of this event.
36675          * @param {Editor} this
36676          * @param {Roo.tree.Node} node 
36677          */
36678         "beforenodeedit" : true
36679     });
36680     
36681     //Roo.log(config);
36682     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36683
36684     this.tree = tree;
36685
36686     tree.on('beforeclick', this.beforeNodeClick, this);
36687     tree.getTreeEl().on('mousedown', this.hide, this);
36688     this.on('complete', this.updateNode, this);
36689     this.on('beforestartedit', this.fitToTree, this);
36690     this.on('startedit', this.bindScroll, this, {delay:10});
36691     this.on('specialkey', this.onSpecialKey, this);
36692 };
36693
36694 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36695     /**
36696      * @cfg {String} alignment
36697      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36698      */
36699     alignment: "l-l",
36700     // inherit
36701     autoSize: false,
36702     /**
36703      * @cfg {Boolean} hideEl
36704      * True to hide the bound element while the editor is displayed (defaults to false)
36705      */
36706     hideEl : false,
36707     /**
36708      * @cfg {String} cls
36709      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36710      */
36711     cls: "x-small-editor x-tree-editor",
36712     /**
36713      * @cfg {Boolean} shim
36714      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36715      */
36716     shim:false,
36717     // inherit
36718     shadow:"frame",
36719     /**
36720      * @cfg {Number} maxWidth
36721      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36722      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36723      * scroll and client offsets into account prior to each edit.
36724      */
36725     maxWidth: 250,
36726
36727     editDelay : 350,
36728
36729     // private
36730     fitToTree : function(ed, el){
36731         var td = this.tree.getTreeEl().dom, nd = el.dom;
36732         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36733             td.scrollLeft = nd.offsetLeft;
36734         }
36735         var w = Math.min(
36736                 this.maxWidth,
36737                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36738         this.setSize(w, '');
36739         
36740         return this.fireEvent('beforenodeedit', this, this.editNode);
36741         
36742     },
36743
36744     // private
36745     triggerEdit : function(node){
36746         this.completeEdit();
36747         this.editNode = node;
36748         this.startEdit(node.ui.textNode, node.text);
36749     },
36750
36751     // private
36752     bindScroll : function(){
36753         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36754     },
36755
36756     // private
36757     beforeNodeClick : function(node, e){
36758         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36759         this.lastClick = new Date();
36760         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36761             e.stopEvent();
36762             this.triggerEdit(node);
36763             return false;
36764         }
36765         return true;
36766     },
36767
36768     // private
36769     updateNode : function(ed, value){
36770         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36771         this.editNode.setText(value);
36772     },
36773
36774     // private
36775     onHide : function(){
36776         Roo.tree.TreeEditor.superclass.onHide.call(this);
36777         if(this.editNode){
36778             this.editNode.ui.focus();
36779         }
36780     },
36781
36782     // private
36783     onSpecialKey : function(field, e){
36784         var k = e.getKey();
36785         if(k == e.ESC){
36786             e.stopEvent();
36787             this.cancelEdit();
36788         }else if(k == e.ENTER && !e.hasModifier()){
36789             e.stopEvent();
36790             this.completeEdit();
36791         }
36792     }
36793 });//<Script type="text/javascript">
36794 /*
36795  * Based on:
36796  * Ext JS Library 1.1.1
36797  * Copyright(c) 2006-2007, Ext JS, LLC.
36798  *
36799  * Originally Released Under LGPL - original licence link has changed is not relivant.
36800  *
36801  * Fork - LGPL
36802  * <script type="text/javascript">
36803  */
36804  
36805 /**
36806  * Not documented??? - probably should be...
36807  */
36808
36809 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36810     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36811     
36812     renderElements : function(n, a, targetNode, bulkRender){
36813         //consel.log("renderElements?");
36814         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36815
36816         var t = n.getOwnerTree();
36817         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36818         
36819         var cols = t.columns;
36820         var bw = t.borderWidth;
36821         var c = cols[0];
36822         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36823          var cb = typeof a.checked == "boolean";
36824         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36825         var colcls = 'x-t-' + tid + '-c0';
36826         var buf = [
36827             '<li class="x-tree-node">',
36828             
36829                 
36830                 '<div class="x-tree-node-el ', a.cls,'">',
36831                     // extran...
36832                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36833                 
36834                 
36835                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36836                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36837                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36838                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36839                            (a.iconCls ? ' '+a.iconCls : ''),
36840                            '" unselectable="on" />',
36841                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36842                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36843                              
36844                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36845                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36846                             '<span unselectable="on" qtip="' + tx + '">',
36847                              tx,
36848                              '</span></a>' ,
36849                     '</div>',
36850                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36851                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36852                  ];
36853         for(var i = 1, len = cols.length; i < len; i++){
36854             c = cols[i];
36855             colcls = 'x-t-' + tid + '-c' +i;
36856             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36857             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36858                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36859                       "</div>");
36860          }
36861          
36862          buf.push(
36863             '</a>',
36864             '<div class="x-clear"></div></div>',
36865             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36866             "</li>");
36867         
36868         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36869             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36870                                 n.nextSibling.ui.getEl(), buf.join(""));
36871         }else{
36872             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36873         }
36874         var el = this.wrap.firstChild;
36875         this.elRow = el;
36876         this.elNode = el.firstChild;
36877         this.ranchor = el.childNodes[1];
36878         this.ctNode = this.wrap.childNodes[1];
36879         var cs = el.firstChild.childNodes;
36880         this.indentNode = cs[0];
36881         this.ecNode = cs[1];
36882         this.iconNode = cs[2];
36883         var index = 3;
36884         if(cb){
36885             this.checkbox = cs[3];
36886             index++;
36887         }
36888         this.anchor = cs[index];
36889         
36890         this.textNode = cs[index].firstChild;
36891         
36892         //el.on("click", this.onClick, this);
36893         //el.on("dblclick", this.onDblClick, this);
36894         
36895         
36896        // console.log(this);
36897     },
36898     initEvents : function(){
36899         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36900         
36901             
36902         var a = this.ranchor;
36903
36904         var el = Roo.get(a);
36905
36906         if(Roo.isOpera){ // opera render bug ignores the CSS
36907             el.setStyle("text-decoration", "none");
36908         }
36909
36910         el.on("click", this.onClick, this);
36911         el.on("dblclick", this.onDblClick, this);
36912         el.on("contextmenu", this.onContextMenu, this);
36913         
36914     },
36915     
36916     /*onSelectedChange : function(state){
36917         if(state){
36918             this.focus();
36919             this.addClass("x-tree-selected");
36920         }else{
36921             //this.blur();
36922             this.removeClass("x-tree-selected");
36923         }
36924     },*/
36925     addClass : function(cls){
36926         if(this.elRow){
36927             Roo.fly(this.elRow).addClass(cls);
36928         }
36929         
36930     },
36931     
36932     
36933     removeClass : function(cls){
36934         if(this.elRow){
36935             Roo.fly(this.elRow).removeClass(cls);
36936         }
36937     }
36938
36939     
36940     
36941 });//<Script type="text/javascript">
36942
36943 /*
36944  * Based on:
36945  * Ext JS Library 1.1.1
36946  * Copyright(c) 2006-2007, Ext JS, LLC.
36947  *
36948  * Originally Released Under LGPL - original licence link has changed is not relivant.
36949  *
36950  * Fork - LGPL
36951  * <script type="text/javascript">
36952  */
36953  
36954
36955 /**
36956  * @class Roo.tree.ColumnTree
36957  * @extends Roo.data.TreePanel
36958  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36959  * @cfg {int} borderWidth  compined right/left border allowance
36960  * @constructor
36961  * @param {String/HTMLElement/Element} el The container element
36962  * @param {Object} config
36963  */
36964 Roo.tree.ColumnTree =  function(el, config)
36965 {
36966    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36967    this.addEvents({
36968         /**
36969         * @event resize
36970         * Fire this event on a container when it resizes
36971         * @param {int} w Width
36972         * @param {int} h Height
36973         */
36974        "resize" : true
36975     });
36976     this.on('resize', this.onResize, this);
36977 };
36978
36979 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36980     //lines:false,
36981     
36982     
36983     borderWidth: Roo.isBorderBox ? 0 : 2, 
36984     headEls : false,
36985     
36986     render : function(){
36987         // add the header.....
36988        
36989         Roo.tree.ColumnTree.superclass.render.apply(this);
36990         
36991         this.el.addClass('x-column-tree');
36992         
36993         this.headers = this.el.createChild(
36994             {cls:'x-tree-headers'},this.innerCt.dom);
36995    
36996         var cols = this.columns, c;
36997         var totalWidth = 0;
36998         this.headEls = [];
36999         var  len = cols.length;
37000         for(var i = 0; i < len; i++){
37001              c = cols[i];
37002              totalWidth += c.width;
37003             this.headEls.push(this.headers.createChild({
37004                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37005                  cn: {
37006                      cls:'x-tree-hd-text',
37007                      html: c.header
37008                  },
37009                  style:'width:'+(c.width-this.borderWidth)+'px;'
37010              }));
37011         }
37012         this.headers.createChild({cls:'x-clear'});
37013         // prevent floats from wrapping when clipped
37014         this.headers.setWidth(totalWidth);
37015         //this.innerCt.setWidth(totalWidth);
37016         this.innerCt.setStyle({ overflow: 'auto' });
37017         this.onResize(this.width, this.height);
37018              
37019         
37020     },
37021     onResize : function(w,h)
37022     {
37023         this.height = h;
37024         this.width = w;
37025         // resize cols..
37026         this.innerCt.setWidth(this.width);
37027         this.innerCt.setHeight(this.height-20);
37028         
37029         // headers...
37030         var cols = this.columns, c;
37031         var totalWidth = 0;
37032         var expEl = false;
37033         var len = cols.length;
37034         for(var i = 0; i < len; i++){
37035             c = cols[i];
37036             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37037                 // it's the expander..
37038                 expEl  = this.headEls[i];
37039                 continue;
37040             }
37041             totalWidth += c.width;
37042             
37043         }
37044         if (expEl) {
37045             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37046         }
37047         this.headers.setWidth(w-20);
37048
37049         
37050         
37051         
37052     }
37053 });
37054 /*
37055  * Based on:
37056  * Ext JS Library 1.1.1
37057  * Copyright(c) 2006-2007, Ext JS, LLC.
37058  *
37059  * Originally Released Under LGPL - original licence link has changed is not relivant.
37060  *
37061  * Fork - LGPL
37062  * <script type="text/javascript">
37063  */
37064  
37065 /**
37066  * @class Roo.menu.Menu
37067  * @extends Roo.util.Observable
37068  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37069  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37070  * @constructor
37071  * Creates a new Menu
37072  * @param {Object} config Configuration options
37073  */
37074 Roo.menu.Menu = function(config){
37075     Roo.apply(this, config);
37076     this.id = this.id || Roo.id();
37077     this.addEvents({
37078         /**
37079          * @event beforeshow
37080          * Fires before this menu is displayed
37081          * @param {Roo.menu.Menu} this
37082          */
37083         beforeshow : true,
37084         /**
37085          * @event beforehide
37086          * Fires before this menu is hidden
37087          * @param {Roo.menu.Menu} this
37088          */
37089         beforehide : true,
37090         /**
37091          * @event show
37092          * Fires after this menu is displayed
37093          * @param {Roo.menu.Menu} this
37094          */
37095         show : true,
37096         /**
37097          * @event hide
37098          * Fires after this menu is hidden
37099          * @param {Roo.menu.Menu} this
37100          */
37101         hide : true,
37102         /**
37103          * @event click
37104          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37105          * @param {Roo.menu.Menu} this
37106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37107          * @param {Roo.EventObject} e
37108          */
37109         click : true,
37110         /**
37111          * @event mouseover
37112          * Fires when the mouse is hovering over this menu
37113          * @param {Roo.menu.Menu} this
37114          * @param {Roo.EventObject} e
37115          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37116          */
37117         mouseover : true,
37118         /**
37119          * @event mouseout
37120          * Fires when the mouse exits this menu
37121          * @param {Roo.menu.Menu} this
37122          * @param {Roo.EventObject} e
37123          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37124          */
37125         mouseout : true,
37126         /**
37127          * @event itemclick
37128          * Fires when a menu item contained in this menu is clicked
37129          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37130          * @param {Roo.EventObject} e
37131          */
37132         itemclick: true
37133     });
37134     if (this.registerMenu) {
37135         Roo.menu.MenuMgr.register(this);
37136     }
37137     
37138     var mis = this.items;
37139     this.items = new Roo.util.MixedCollection();
37140     if(mis){
37141         this.add.apply(this, mis);
37142     }
37143 };
37144
37145 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37146     /**
37147      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37148      */
37149     minWidth : 120,
37150     /**
37151      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37152      * for bottom-right shadow (defaults to "sides")
37153      */
37154     shadow : "sides",
37155     /**
37156      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37157      * this menu (defaults to "tl-tr?")
37158      */
37159     subMenuAlign : "tl-tr?",
37160     /**
37161      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37162      * relative to its element of origin (defaults to "tl-bl?")
37163      */
37164     defaultAlign : "tl-bl?",
37165     /**
37166      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37167      */
37168     allowOtherMenus : false,
37169     /**
37170      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37171      */
37172     registerMenu : true,
37173
37174     hidden:true,
37175
37176     // private
37177     render : function(){
37178         if(this.el){
37179             return;
37180         }
37181         var el = this.el = new Roo.Layer({
37182             cls: "x-menu",
37183             shadow:this.shadow,
37184             constrain: false,
37185             parentEl: this.parentEl || document.body,
37186             zindex:15000
37187         });
37188
37189         this.keyNav = new Roo.menu.MenuNav(this);
37190
37191         if(this.plain){
37192             el.addClass("x-menu-plain");
37193         }
37194         if(this.cls){
37195             el.addClass(this.cls);
37196         }
37197         // generic focus element
37198         this.focusEl = el.createChild({
37199             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37200         });
37201         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37202         //disabling touch- as it's causing issues ..
37203         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37204         ul.on('click'   , this.onClick, this);
37205         
37206         
37207         ul.on("mouseover", this.onMouseOver, this);
37208         ul.on("mouseout", this.onMouseOut, this);
37209         this.items.each(function(item){
37210             if (item.hidden) {
37211                 return;
37212             }
37213             
37214             var li = document.createElement("li");
37215             li.className = "x-menu-list-item";
37216             ul.dom.appendChild(li);
37217             item.render(li, this);
37218         }, this);
37219         this.ul = ul;
37220         this.autoWidth();
37221     },
37222
37223     // private
37224     autoWidth : function(){
37225         var el = this.el, ul = this.ul;
37226         if(!el){
37227             return;
37228         }
37229         var w = this.width;
37230         if(w){
37231             el.setWidth(w);
37232         }else if(Roo.isIE){
37233             el.setWidth(this.minWidth);
37234             var t = el.dom.offsetWidth; // force recalc
37235             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37236         }
37237     },
37238
37239     // private
37240     delayAutoWidth : function(){
37241         if(this.rendered){
37242             if(!this.awTask){
37243                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37244             }
37245             this.awTask.delay(20);
37246         }
37247     },
37248
37249     // private
37250     findTargetItem : function(e){
37251         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37252         if(t && t.menuItemId){
37253             return this.items.get(t.menuItemId);
37254         }
37255     },
37256
37257     // private
37258     onClick : function(e){
37259         Roo.log("menu.onClick");
37260         var t = this.findTargetItem(e);
37261         if(!t){
37262             return;
37263         }
37264         Roo.log(e);
37265         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37266             if(t == this.activeItem && t.shouldDeactivate(e)){
37267                 this.activeItem.deactivate();
37268                 delete this.activeItem;
37269                 return;
37270             }
37271             if(t.canActivate){
37272                 this.setActiveItem(t, true);
37273             }
37274             return;
37275             
37276             
37277         }
37278         
37279         t.onClick(e);
37280         this.fireEvent("click", this, t, e);
37281     },
37282
37283     // private
37284     setActiveItem : function(item, autoExpand){
37285         if(item != this.activeItem){
37286             if(this.activeItem){
37287                 this.activeItem.deactivate();
37288             }
37289             this.activeItem = item;
37290             item.activate(autoExpand);
37291         }else if(autoExpand){
37292             item.expandMenu();
37293         }
37294     },
37295
37296     // private
37297     tryActivate : function(start, step){
37298         var items = this.items;
37299         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37300             var item = items.get(i);
37301             if(!item.disabled && item.canActivate){
37302                 this.setActiveItem(item, false);
37303                 return item;
37304             }
37305         }
37306         return false;
37307     },
37308
37309     // private
37310     onMouseOver : function(e){
37311         var t;
37312         if(t = this.findTargetItem(e)){
37313             if(t.canActivate && !t.disabled){
37314                 this.setActiveItem(t, true);
37315             }
37316         }
37317         this.fireEvent("mouseover", this, e, t);
37318     },
37319
37320     // private
37321     onMouseOut : function(e){
37322         var t;
37323         if(t = this.findTargetItem(e)){
37324             if(t == this.activeItem && t.shouldDeactivate(e)){
37325                 this.activeItem.deactivate();
37326                 delete this.activeItem;
37327             }
37328         }
37329         this.fireEvent("mouseout", this, e, t);
37330     },
37331
37332     /**
37333      * Read-only.  Returns true if the menu is currently displayed, else false.
37334      * @type Boolean
37335      */
37336     isVisible : function(){
37337         return this.el && !this.hidden;
37338     },
37339
37340     /**
37341      * Displays this menu relative to another element
37342      * @param {String/HTMLElement/Roo.Element} element The element to align to
37343      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37344      * the element (defaults to this.defaultAlign)
37345      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37346      */
37347     show : function(el, pos, parentMenu){
37348         this.parentMenu = parentMenu;
37349         if(!this.el){
37350             this.render();
37351         }
37352         this.fireEvent("beforeshow", this);
37353         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37354     },
37355
37356     /**
37357      * Displays this menu at a specific xy position
37358      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37359      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37360      */
37361     showAt : function(xy, parentMenu, /* private: */_e){
37362         this.parentMenu = parentMenu;
37363         if(!this.el){
37364             this.render();
37365         }
37366         if(_e !== false){
37367             this.fireEvent("beforeshow", this);
37368             xy = this.el.adjustForConstraints(xy);
37369         }
37370         this.el.setXY(xy);
37371         this.el.show();
37372         this.hidden = false;
37373         this.focus();
37374         this.fireEvent("show", this);
37375     },
37376
37377     focus : function(){
37378         if(!this.hidden){
37379             this.doFocus.defer(50, this);
37380         }
37381     },
37382
37383     doFocus : function(){
37384         if(!this.hidden){
37385             this.focusEl.focus();
37386         }
37387     },
37388
37389     /**
37390      * Hides this menu and optionally all parent menus
37391      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37392      */
37393     hide : function(deep){
37394         if(this.el && this.isVisible()){
37395             this.fireEvent("beforehide", this);
37396             if(this.activeItem){
37397                 this.activeItem.deactivate();
37398                 this.activeItem = null;
37399             }
37400             this.el.hide();
37401             this.hidden = true;
37402             this.fireEvent("hide", this);
37403         }
37404         if(deep === true && this.parentMenu){
37405             this.parentMenu.hide(true);
37406         }
37407     },
37408
37409     /**
37410      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37411      * Any of the following are valid:
37412      * <ul>
37413      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37414      * <li>An HTMLElement object which will be converted to a menu item</li>
37415      * <li>A menu item config object that will be created as a new menu item</li>
37416      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37417      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37418      * </ul>
37419      * Usage:
37420      * <pre><code>
37421 // Create the menu
37422 var menu = new Roo.menu.Menu();
37423
37424 // Create a menu item to add by reference
37425 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37426
37427 // Add a bunch of items at once using different methods.
37428 // Only the last item added will be returned.
37429 var item = menu.add(
37430     menuItem,                // add existing item by ref
37431     'Dynamic Item',          // new TextItem
37432     '-',                     // new separator
37433     { text: 'Config Item' }  // new item by config
37434 );
37435 </code></pre>
37436      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37437      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37438      */
37439     add : function(){
37440         var a = arguments, l = a.length, item;
37441         for(var i = 0; i < l; i++){
37442             var el = a[i];
37443             if ((typeof(el) == "object") && el.xtype && el.xns) {
37444                 el = Roo.factory(el, Roo.menu);
37445             }
37446             
37447             if(el.render){ // some kind of Item
37448                 item = this.addItem(el);
37449             }else if(typeof el == "string"){ // string
37450                 if(el == "separator" || el == "-"){
37451                     item = this.addSeparator();
37452                 }else{
37453                     item = this.addText(el);
37454                 }
37455             }else if(el.tagName || el.el){ // element
37456                 item = this.addElement(el);
37457             }else if(typeof el == "object"){ // must be menu item config?
37458                 item = this.addMenuItem(el);
37459             }
37460         }
37461         return item;
37462     },
37463
37464     /**
37465      * Returns this menu's underlying {@link Roo.Element} object
37466      * @return {Roo.Element} The element
37467      */
37468     getEl : function(){
37469         if(!this.el){
37470             this.render();
37471         }
37472         return this.el;
37473     },
37474
37475     /**
37476      * Adds a separator bar to the menu
37477      * @return {Roo.menu.Item} The menu item that was added
37478      */
37479     addSeparator : function(){
37480         return this.addItem(new Roo.menu.Separator());
37481     },
37482
37483     /**
37484      * Adds an {@link Roo.Element} object to the menu
37485      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37486      * @return {Roo.menu.Item} The menu item that was added
37487      */
37488     addElement : function(el){
37489         return this.addItem(new Roo.menu.BaseItem(el));
37490     },
37491
37492     /**
37493      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37494      * @param {Roo.menu.Item} item The menu item to add
37495      * @return {Roo.menu.Item} The menu item that was added
37496      */
37497     addItem : function(item){
37498         this.items.add(item);
37499         if(this.ul){
37500             var li = document.createElement("li");
37501             li.className = "x-menu-list-item";
37502             this.ul.dom.appendChild(li);
37503             item.render(li, this);
37504             this.delayAutoWidth();
37505         }
37506         return item;
37507     },
37508
37509     /**
37510      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37511      * @param {Object} config A MenuItem config object
37512      * @return {Roo.menu.Item} The menu item that was added
37513      */
37514     addMenuItem : function(config){
37515         if(!(config instanceof Roo.menu.Item)){
37516             if(typeof config.checked == "boolean"){ // must be check menu item config?
37517                 config = new Roo.menu.CheckItem(config);
37518             }else{
37519                 config = new Roo.menu.Item(config);
37520             }
37521         }
37522         return this.addItem(config);
37523     },
37524
37525     /**
37526      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37527      * @param {String} text The text to display in the menu item
37528      * @return {Roo.menu.Item} The menu item that was added
37529      */
37530     addText : function(text){
37531         return this.addItem(new Roo.menu.TextItem({ text : text }));
37532     },
37533
37534     /**
37535      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37536      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37537      * @param {Roo.menu.Item} item The menu item to add
37538      * @return {Roo.menu.Item} The menu item that was added
37539      */
37540     insert : function(index, item){
37541         this.items.insert(index, item);
37542         if(this.ul){
37543             var li = document.createElement("li");
37544             li.className = "x-menu-list-item";
37545             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37546             item.render(li, this);
37547             this.delayAutoWidth();
37548         }
37549         return item;
37550     },
37551
37552     /**
37553      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37554      * @param {Roo.menu.Item} item The menu item to remove
37555      */
37556     remove : function(item){
37557         this.items.removeKey(item.id);
37558         item.destroy();
37559     },
37560
37561     /**
37562      * Removes and destroys all items in the menu
37563      */
37564     removeAll : function(){
37565         var f;
37566         while(f = this.items.first()){
37567             this.remove(f);
37568         }
37569     }
37570 });
37571
37572 // MenuNav is a private utility class used internally by the Menu
37573 Roo.menu.MenuNav = function(menu){
37574     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37575     this.scope = this.menu = menu;
37576 };
37577
37578 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37579     doRelay : function(e, h){
37580         var k = e.getKey();
37581         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37582             this.menu.tryActivate(0, 1);
37583             return false;
37584         }
37585         return h.call(this.scope || this, e, this.menu);
37586     },
37587
37588     up : function(e, m){
37589         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37590             m.tryActivate(m.items.length-1, -1);
37591         }
37592     },
37593
37594     down : function(e, m){
37595         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37596             m.tryActivate(0, 1);
37597         }
37598     },
37599
37600     right : function(e, m){
37601         if(m.activeItem){
37602             m.activeItem.expandMenu(true);
37603         }
37604     },
37605
37606     left : function(e, m){
37607         m.hide();
37608         if(m.parentMenu && m.parentMenu.activeItem){
37609             m.parentMenu.activeItem.activate();
37610         }
37611     },
37612
37613     enter : function(e, m){
37614         if(m.activeItem){
37615             e.stopPropagation();
37616             m.activeItem.onClick(e);
37617             m.fireEvent("click", this, m.activeItem);
37618             return true;
37619         }
37620     }
37621 });/*
37622  * Based on:
37623  * Ext JS Library 1.1.1
37624  * Copyright(c) 2006-2007, Ext JS, LLC.
37625  *
37626  * Originally Released Under LGPL - original licence link has changed is not relivant.
37627  *
37628  * Fork - LGPL
37629  * <script type="text/javascript">
37630  */
37631  
37632 /**
37633  * @class Roo.menu.MenuMgr
37634  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37635  * @singleton
37636  */
37637 Roo.menu.MenuMgr = function(){
37638    var menus, active, groups = {}, attached = false, lastShow = new Date();
37639
37640    // private - called when first menu is created
37641    function init(){
37642        menus = {};
37643        active = new Roo.util.MixedCollection();
37644        Roo.get(document).addKeyListener(27, function(){
37645            if(active.length > 0){
37646                hideAll();
37647            }
37648        });
37649    }
37650
37651    // private
37652    function hideAll(){
37653        if(active && active.length > 0){
37654            var c = active.clone();
37655            c.each(function(m){
37656                m.hide();
37657            });
37658        }
37659    }
37660
37661    // private
37662    function onHide(m){
37663        active.remove(m);
37664        if(active.length < 1){
37665            Roo.get(document).un("mousedown", onMouseDown);
37666            attached = false;
37667        }
37668    }
37669
37670    // private
37671    function onShow(m){
37672        var last = active.last();
37673        lastShow = new Date();
37674        active.add(m);
37675        if(!attached){
37676            Roo.get(document).on("mousedown", onMouseDown);
37677            attached = true;
37678        }
37679        if(m.parentMenu){
37680           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37681           m.parentMenu.activeChild = m;
37682        }else if(last && last.isVisible()){
37683           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37684        }
37685    }
37686
37687    // private
37688    function onBeforeHide(m){
37689        if(m.activeChild){
37690            m.activeChild.hide();
37691        }
37692        if(m.autoHideTimer){
37693            clearTimeout(m.autoHideTimer);
37694            delete m.autoHideTimer;
37695        }
37696    }
37697
37698    // private
37699    function onBeforeShow(m){
37700        var pm = m.parentMenu;
37701        if(!pm && !m.allowOtherMenus){
37702            hideAll();
37703        }else if(pm && pm.activeChild && active != m){
37704            pm.activeChild.hide();
37705        }
37706    }
37707
37708    // private
37709    function onMouseDown(e){
37710        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37711            hideAll();
37712        }
37713    }
37714
37715    // private
37716    function onBeforeCheck(mi, state){
37717        if(state){
37718            var g = groups[mi.group];
37719            for(var i = 0, l = g.length; i < l; i++){
37720                if(g[i] != mi){
37721                    g[i].setChecked(false);
37722                }
37723            }
37724        }
37725    }
37726
37727    return {
37728
37729        /**
37730         * Hides all menus that are currently visible
37731         */
37732        hideAll : function(){
37733             hideAll();  
37734        },
37735
37736        // private
37737        register : function(menu){
37738            if(!menus){
37739                init();
37740            }
37741            menus[menu.id] = menu;
37742            menu.on("beforehide", onBeforeHide);
37743            menu.on("hide", onHide);
37744            menu.on("beforeshow", onBeforeShow);
37745            menu.on("show", onShow);
37746            var g = menu.group;
37747            if(g && menu.events["checkchange"]){
37748                if(!groups[g]){
37749                    groups[g] = [];
37750                }
37751                groups[g].push(menu);
37752                menu.on("checkchange", onCheck);
37753            }
37754        },
37755
37756         /**
37757          * Returns a {@link Roo.menu.Menu} object
37758          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37759          * be used to generate and return a new Menu instance.
37760          */
37761        get : function(menu){
37762            if(typeof menu == "string"){ // menu id
37763                return menus[menu];
37764            }else if(menu.events){  // menu instance
37765                return menu;
37766            }else if(typeof menu.length == 'number'){ // array of menu items?
37767                return new Roo.menu.Menu({items:menu});
37768            }else{ // otherwise, must be a config
37769                return new Roo.menu.Menu(menu);
37770            }
37771        },
37772
37773        // private
37774        unregister : function(menu){
37775            delete menus[menu.id];
37776            menu.un("beforehide", onBeforeHide);
37777            menu.un("hide", onHide);
37778            menu.un("beforeshow", onBeforeShow);
37779            menu.un("show", onShow);
37780            var g = menu.group;
37781            if(g && menu.events["checkchange"]){
37782                groups[g].remove(menu);
37783                menu.un("checkchange", onCheck);
37784            }
37785        },
37786
37787        // private
37788        registerCheckable : function(menuItem){
37789            var g = menuItem.group;
37790            if(g){
37791                if(!groups[g]){
37792                    groups[g] = [];
37793                }
37794                groups[g].push(menuItem);
37795                menuItem.on("beforecheckchange", onBeforeCheck);
37796            }
37797        },
37798
37799        // private
37800        unregisterCheckable : function(menuItem){
37801            var g = menuItem.group;
37802            if(g){
37803                groups[g].remove(menuItem);
37804                menuItem.un("beforecheckchange", onBeforeCheck);
37805            }
37806        }
37807    };
37808 }();/*
37809  * Based on:
37810  * Ext JS Library 1.1.1
37811  * Copyright(c) 2006-2007, Ext JS, LLC.
37812  *
37813  * Originally Released Under LGPL - original licence link has changed is not relivant.
37814  *
37815  * Fork - LGPL
37816  * <script type="text/javascript">
37817  */
37818  
37819
37820 /**
37821  * @class Roo.menu.BaseItem
37822  * @extends Roo.Component
37823  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37824  * management and base configuration options shared by all menu components.
37825  * @constructor
37826  * Creates a new BaseItem
37827  * @param {Object} config Configuration options
37828  */
37829 Roo.menu.BaseItem = function(config){
37830     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37831
37832     this.addEvents({
37833         /**
37834          * @event click
37835          * Fires when this item is clicked
37836          * @param {Roo.menu.BaseItem} this
37837          * @param {Roo.EventObject} e
37838          */
37839         click: true,
37840         /**
37841          * @event activate
37842          * Fires when this item is activated
37843          * @param {Roo.menu.BaseItem} this
37844          */
37845         activate : true,
37846         /**
37847          * @event deactivate
37848          * Fires when this item is deactivated
37849          * @param {Roo.menu.BaseItem} this
37850          */
37851         deactivate : true
37852     });
37853
37854     if(this.handler){
37855         this.on("click", this.handler, this.scope, true);
37856     }
37857 };
37858
37859 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37860     /**
37861      * @cfg {Function} handler
37862      * A function that will handle the click event of this menu item (defaults to undefined)
37863      */
37864     /**
37865      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37866      */
37867     canActivate : false,
37868     
37869      /**
37870      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37871      */
37872     hidden: false,
37873     
37874     /**
37875      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37876      */
37877     activeClass : "x-menu-item-active",
37878     /**
37879      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37880      */
37881     hideOnClick : true,
37882     /**
37883      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37884      */
37885     hideDelay : 100,
37886
37887     // private
37888     ctype: "Roo.menu.BaseItem",
37889
37890     // private
37891     actionMode : "container",
37892
37893     // private
37894     render : function(container, parentMenu){
37895         this.parentMenu = parentMenu;
37896         Roo.menu.BaseItem.superclass.render.call(this, container);
37897         this.container.menuItemId = this.id;
37898     },
37899
37900     // private
37901     onRender : function(container, position){
37902         this.el = Roo.get(this.el);
37903         container.dom.appendChild(this.el.dom);
37904     },
37905
37906     // private
37907     onClick : function(e){
37908         if(!this.disabled && this.fireEvent("click", this, e) !== false
37909                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37910             this.handleClick(e);
37911         }else{
37912             e.stopEvent();
37913         }
37914     },
37915
37916     // private
37917     activate : function(){
37918         if(this.disabled){
37919             return false;
37920         }
37921         var li = this.container;
37922         li.addClass(this.activeClass);
37923         this.region = li.getRegion().adjust(2, 2, -2, -2);
37924         this.fireEvent("activate", this);
37925         return true;
37926     },
37927
37928     // private
37929     deactivate : function(){
37930         this.container.removeClass(this.activeClass);
37931         this.fireEvent("deactivate", this);
37932     },
37933
37934     // private
37935     shouldDeactivate : function(e){
37936         return !this.region || !this.region.contains(e.getPoint());
37937     },
37938
37939     // private
37940     handleClick : function(e){
37941         if(this.hideOnClick){
37942             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37943         }
37944     },
37945
37946     // private
37947     expandMenu : function(autoActivate){
37948         // do nothing
37949     },
37950
37951     // private
37952     hideMenu : function(){
37953         // do nothing
37954     }
37955 });/*
37956  * Based on:
37957  * Ext JS Library 1.1.1
37958  * Copyright(c) 2006-2007, Ext JS, LLC.
37959  *
37960  * Originally Released Under LGPL - original licence link has changed is not relivant.
37961  *
37962  * Fork - LGPL
37963  * <script type="text/javascript">
37964  */
37965  
37966 /**
37967  * @class Roo.menu.Adapter
37968  * @extends Roo.menu.BaseItem
37969  * 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.
37970  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37971  * @constructor
37972  * Creates a new Adapter
37973  * @param {Object} config Configuration options
37974  */
37975 Roo.menu.Adapter = function(component, config){
37976     Roo.menu.Adapter.superclass.constructor.call(this, config);
37977     this.component = component;
37978 };
37979 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37980     // private
37981     canActivate : true,
37982
37983     // private
37984     onRender : function(container, position){
37985         this.component.render(container);
37986         this.el = this.component.getEl();
37987     },
37988
37989     // private
37990     activate : function(){
37991         if(this.disabled){
37992             return false;
37993         }
37994         this.component.focus();
37995         this.fireEvent("activate", this);
37996         return true;
37997     },
37998
37999     // private
38000     deactivate : function(){
38001         this.fireEvent("deactivate", this);
38002     },
38003
38004     // private
38005     disable : function(){
38006         this.component.disable();
38007         Roo.menu.Adapter.superclass.disable.call(this);
38008     },
38009
38010     // private
38011     enable : function(){
38012         this.component.enable();
38013         Roo.menu.Adapter.superclass.enable.call(this);
38014     }
38015 });/*
38016  * Based on:
38017  * Ext JS Library 1.1.1
38018  * Copyright(c) 2006-2007, Ext JS, LLC.
38019  *
38020  * Originally Released Under LGPL - original licence link has changed is not relivant.
38021  *
38022  * Fork - LGPL
38023  * <script type="text/javascript">
38024  */
38025
38026 /**
38027  * @class Roo.menu.TextItem
38028  * @extends Roo.menu.BaseItem
38029  * Adds a static text string to a menu, usually used as either a heading or group separator.
38030  * Note: old style constructor with text is still supported.
38031  * 
38032  * @constructor
38033  * Creates a new TextItem
38034  * @param {Object} cfg Configuration
38035  */
38036 Roo.menu.TextItem = function(cfg){
38037     if (typeof(cfg) == 'string') {
38038         this.text = cfg;
38039     } else {
38040         Roo.apply(this,cfg);
38041     }
38042     
38043     Roo.menu.TextItem.superclass.constructor.call(this);
38044 };
38045
38046 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38047     /**
38048      * @cfg {Boolean} text Text to show on item.
38049      */
38050     text : '',
38051     
38052     /**
38053      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38054      */
38055     hideOnClick : false,
38056     /**
38057      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38058      */
38059     itemCls : "x-menu-text",
38060
38061     // private
38062     onRender : function(){
38063         var s = document.createElement("span");
38064         s.className = this.itemCls;
38065         s.innerHTML = this.text;
38066         this.el = s;
38067         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38068     }
38069 });/*
38070  * Based on:
38071  * Ext JS Library 1.1.1
38072  * Copyright(c) 2006-2007, Ext JS, LLC.
38073  *
38074  * Originally Released Under LGPL - original licence link has changed is not relivant.
38075  *
38076  * Fork - LGPL
38077  * <script type="text/javascript">
38078  */
38079
38080 /**
38081  * @class Roo.menu.Separator
38082  * @extends Roo.menu.BaseItem
38083  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38084  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38085  * @constructor
38086  * @param {Object} config Configuration options
38087  */
38088 Roo.menu.Separator = function(config){
38089     Roo.menu.Separator.superclass.constructor.call(this, config);
38090 };
38091
38092 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38093     /**
38094      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38095      */
38096     itemCls : "x-menu-sep",
38097     /**
38098      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38099      */
38100     hideOnClick : false,
38101
38102     // private
38103     onRender : function(li){
38104         var s = document.createElement("span");
38105         s.className = this.itemCls;
38106         s.innerHTML = "&#160;";
38107         this.el = s;
38108         li.addClass("x-menu-sep-li");
38109         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38110     }
38111 });/*
38112  * Based on:
38113  * Ext JS Library 1.1.1
38114  * Copyright(c) 2006-2007, Ext JS, LLC.
38115  *
38116  * Originally Released Under LGPL - original licence link has changed is not relivant.
38117  *
38118  * Fork - LGPL
38119  * <script type="text/javascript">
38120  */
38121 /**
38122  * @class Roo.menu.Item
38123  * @extends Roo.menu.BaseItem
38124  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38125  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38126  * activation and click handling.
38127  * @constructor
38128  * Creates a new Item
38129  * @param {Object} config Configuration options
38130  */
38131 Roo.menu.Item = function(config){
38132     Roo.menu.Item.superclass.constructor.call(this, config);
38133     if(this.menu){
38134         this.menu = Roo.menu.MenuMgr.get(this.menu);
38135     }
38136 };
38137 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38138     
38139     /**
38140      * @cfg {String} text
38141      * The text to show on the menu item.
38142      */
38143     text: '',
38144      /**
38145      * @cfg {String} HTML to render in menu
38146      * The text to show on the menu item (HTML version).
38147      */
38148     html: '',
38149     /**
38150      * @cfg {String} icon
38151      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38152      */
38153     icon: undefined,
38154     /**
38155      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38156      */
38157     itemCls : "x-menu-item",
38158     /**
38159      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38160      */
38161     canActivate : true,
38162     /**
38163      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38164      */
38165     showDelay: 200,
38166     // doc'd in BaseItem
38167     hideDelay: 200,
38168
38169     // private
38170     ctype: "Roo.menu.Item",
38171     
38172     // private
38173     onRender : function(container, position){
38174         var el = document.createElement("a");
38175         el.hideFocus = true;
38176         el.unselectable = "on";
38177         el.href = this.href || "#";
38178         if(this.hrefTarget){
38179             el.target = this.hrefTarget;
38180         }
38181         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38182         
38183         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38184         
38185         el.innerHTML = String.format(
38186                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38187                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38188         this.el = el;
38189         Roo.menu.Item.superclass.onRender.call(this, container, position);
38190     },
38191
38192     /**
38193      * Sets the text to display in this menu item
38194      * @param {String} text The text to display
38195      * @param {Boolean} isHTML true to indicate text is pure html.
38196      */
38197     setText : function(text, isHTML){
38198         if (isHTML) {
38199             this.html = text;
38200         } else {
38201             this.text = text;
38202             this.html = '';
38203         }
38204         if(this.rendered){
38205             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38206      
38207             this.el.update(String.format(
38208                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38209                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38210             this.parentMenu.autoWidth();
38211         }
38212     },
38213
38214     // private
38215     handleClick : function(e){
38216         if(!this.href){ // if no link defined, stop the event automatically
38217             e.stopEvent();
38218         }
38219         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38220     },
38221
38222     // private
38223     activate : function(autoExpand){
38224         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38225             this.focus();
38226             if(autoExpand){
38227                 this.expandMenu();
38228             }
38229         }
38230         return true;
38231     },
38232
38233     // private
38234     shouldDeactivate : function(e){
38235         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38236             if(this.menu && this.menu.isVisible()){
38237                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38238             }
38239             return true;
38240         }
38241         return false;
38242     },
38243
38244     // private
38245     deactivate : function(){
38246         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38247         this.hideMenu();
38248     },
38249
38250     // private
38251     expandMenu : function(autoActivate){
38252         if(!this.disabled && this.menu){
38253             clearTimeout(this.hideTimer);
38254             delete this.hideTimer;
38255             if(!this.menu.isVisible() && !this.showTimer){
38256                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38257             }else if (this.menu.isVisible() && autoActivate){
38258                 this.menu.tryActivate(0, 1);
38259             }
38260         }
38261     },
38262
38263     // private
38264     deferExpand : function(autoActivate){
38265         delete this.showTimer;
38266         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38267         if(autoActivate){
38268             this.menu.tryActivate(0, 1);
38269         }
38270     },
38271
38272     // private
38273     hideMenu : function(){
38274         clearTimeout(this.showTimer);
38275         delete this.showTimer;
38276         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38277             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38278         }
38279     },
38280
38281     // private
38282     deferHide : function(){
38283         delete this.hideTimer;
38284         this.menu.hide();
38285     }
38286 });/*
38287  * Based on:
38288  * Ext JS Library 1.1.1
38289  * Copyright(c) 2006-2007, Ext JS, LLC.
38290  *
38291  * Originally Released Under LGPL - original licence link has changed is not relivant.
38292  *
38293  * Fork - LGPL
38294  * <script type="text/javascript">
38295  */
38296  
38297 /**
38298  * @class Roo.menu.CheckItem
38299  * @extends Roo.menu.Item
38300  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38301  * @constructor
38302  * Creates a new CheckItem
38303  * @param {Object} config Configuration options
38304  */
38305 Roo.menu.CheckItem = function(config){
38306     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38307     this.addEvents({
38308         /**
38309          * @event beforecheckchange
38310          * Fires before the checked value is set, providing an opportunity to cancel if needed
38311          * @param {Roo.menu.CheckItem} this
38312          * @param {Boolean} checked The new checked value that will be set
38313          */
38314         "beforecheckchange" : true,
38315         /**
38316          * @event checkchange
38317          * Fires after the checked value has been set
38318          * @param {Roo.menu.CheckItem} this
38319          * @param {Boolean} checked The checked value that was set
38320          */
38321         "checkchange" : true
38322     });
38323     if(this.checkHandler){
38324         this.on('checkchange', this.checkHandler, this.scope);
38325     }
38326 };
38327 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38328     /**
38329      * @cfg {String} group
38330      * All check items with the same group name will automatically be grouped into a single-select
38331      * radio button group (defaults to '')
38332      */
38333     /**
38334      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38335      */
38336     itemCls : "x-menu-item x-menu-check-item",
38337     /**
38338      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38339      */
38340     groupClass : "x-menu-group-item",
38341
38342     /**
38343      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38344      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38345      * initialized with checked = true will be rendered as checked.
38346      */
38347     checked: false,
38348
38349     // private
38350     ctype: "Roo.menu.CheckItem",
38351
38352     // private
38353     onRender : function(c){
38354         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38355         if(this.group){
38356             this.el.addClass(this.groupClass);
38357         }
38358         Roo.menu.MenuMgr.registerCheckable(this);
38359         if(this.checked){
38360             this.checked = false;
38361             this.setChecked(true, true);
38362         }
38363     },
38364
38365     // private
38366     destroy : function(){
38367         if(this.rendered){
38368             Roo.menu.MenuMgr.unregisterCheckable(this);
38369         }
38370         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38371     },
38372
38373     /**
38374      * Set the checked state of this item
38375      * @param {Boolean} checked The new checked value
38376      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38377      */
38378     setChecked : function(state, suppressEvent){
38379         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38380             if(this.container){
38381                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38382             }
38383             this.checked = state;
38384             if(suppressEvent !== true){
38385                 this.fireEvent("checkchange", this, state);
38386             }
38387         }
38388     },
38389
38390     // private
38391     handleClick : function(e){
38392        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38393            this.setChecked(!this.checked);
38394        }
38395        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38396     }
38397 });/*
38398  * Based on:
38399  * Ext JS Library 1.1.1
38400  * Copyright(c) 2006-2007, Ext JS, LLC.
38401  *
38402  * Originally Released Under LGPL - original licence link has changed is not relivant.
38403  *
38404  * Fork - LGPL
38405  * <script type="text/javascript">
38406  */
38407  
38408 /**
38409  * @class Roo.menu.DateItem
38410  * @extends Roo.menu.Adapter
38411  * A menu item that wraps the {@link Roo.DatPicker} component.
38412  * @constructor
38413  * Creates a new DateItem
38414  * @param {Object} config Configuration options
38415  */
38416 Roo.menu.DateItem = function(config){
38417     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38418     /** The Roo.DatePicker object @type Roo.DatePicker */
38419     this.picker = this.component;
38420     this.addEvents({select: true});
38421     
38422     this.picker.on("render", function(picker){
38423         picker.getEl().swallowEvent("click");
38424         picker.container.addClass("x-menu-date-item");
38425     });
38426
38427     this.picker.on("select", this.onSelect, this);
38428 };
38429
38430 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38431     // private
38432     onSelect : function(picker, date){
38433         this.fireEvent("select", this, date, picker);
38434         Roo.menu.DateItem.superclass.handleClick.call(this);
38435     }
38436 });/*
38437  * Based on:
38438  * Ext JS Library 1.1.1
38439  * Copyright(c) 2006-2007, Ext JS, LLC.
38440  *
38441  * Originally Released Under LGPL - original licence link has changed is not relivant.
38442  *
38443  * Fork - LGPL
38444  * <script type="text/javascript">
38445  */
38446  
38447 /**
38448  * @class Roo.menu.ColorItem
38449  * @extends Roo.menu.Adapter
38450  * A menu item that wraps the {@link Roo.ColorPalette} component.
38451  * @constructor
38452  * Creates a new ColorItem
38453  * @param {Object} config Configuration options
38454  */
38455 Roo.menu.ColorItem = function(config){
38456     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38457     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38458     this.palette = this.component;
38459     this.relayEvents(this.palette, ["select"]);
38460     if(this.selectHandler){
38461         this.on('select', this.selectHandler, this.scope);
38462     }
38463 };
38464 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38465  * Based on:
38466  * Ext JS Library 1.1.1
38467  * Copyright(c) 2006-2007, Ext JS, LLC.
38468  *
38469  * Originally Released Under LGPL - original licence link has changed is not relivant.
38470  *
38471  * Fork - LGPL
38472  * <script type="text/javascript">
38473  */
38474  
38475
38476 /**
38477  * @class Roo.menu.DateMenu
38478  * @extends Roo.menu.Menu
38479  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38480  * @constructor
38481  * Creates a new DateMenu
38482  * @param {Object} config Configuration options
38483  */
38484 Roo.menu.DateMenu = function(config){
38485     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38486     this.plain = true;
38487     var di = new Roo.menu.DateItem(config);
38488     this.add(di);
38489     /**
38490      * The {@link Roo.DatePicker} instance for this DateMenu
38491      * @type DatePicker
38492      */
38493     this.picker = di.picker;
38494     /**
38495      * @event select
38496      * @param {DatePicker} picker
38497      * @param {Date} date
38498      */
38499     this.relayEvents(di, ["select"]);
38500     this.on('beforeshow', function(){
38501         if(this.picker){
38502             this.picker.hideMonthPicker(false);
38503         }
38504     }, this);
38505 };
38506 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38507     cls:'x-date-menu'
38508 });/*
38509  * Based on:
38510  * Ext JS Library 1.1.1
38511  * Copyright(c) 2006-2007, Ext JS, LLC.
38512  *
38513  * Originally Released Under LGPL - original licence link has changed is not relivant.
38514  *
38515  * Fork - LGPL
38516  * <script type="text/javascript">
38517  */
38518  
38519
38520 /**
38521  * @class Roo.menu.ColorMenu
38522  * @extends Roo.menu.Menu
38523  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38524  * @constructor
38525  * Creates a new ColorMenu
38526  * @param {Object} config Configuration options
38527  */
38528 Roo.menu.ColorMenu = function(config){
38529     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38530     this.plain = true;
38531     var ci = new Roo.menu.ColorItem(config);
38532     this.add(ci);
38533     /**
38534      * The {@link Roo.ColorPalette} instance for this ColorMenu
38535      * @type ColorPalette
38536      */
38537     this.palette = ci.palette;
38538     /**
38539      * @event select
38540      * @param {ColorPalette} palette
38541      * @param {String} color
38542      */
38543     this.relayEvents(ci, ["select"]);
38544 };
38545 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38546  * Based on:
38547  * Ext JS Library 1.1.1
38548  * Copyright(c) 2006-2007, Ext JS, LLC.
38549  *
38550  * Originally Released Under LGPL - original licence link has changed is not relivant.
38551  *
38552  * Fork - LGPL
38553  * <script type="text/javascript">
38554  */
38555  
38556 /**
38557  * @class Roo.form.Field
38558  * @extends Roo.BoxComponent
38559  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38560  * @constructor
38561  * Creates a new Field
38562  * @param {Object} config Configuration options
38563  */
38564 Roo.form.Field = function(config){
38565     Roo.form.Field.superclass.constructor.call(this, config);
38566 };
38567
38568 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38569     /**
38570      * @cfg {String} fieldLabel Label to use when rendering a form.
38571      */
38572        /**
38573      * @cfg {String} qtip Mouse over tip
38574      */
38575      
38576     /**
38577      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38578      */
38579     invalidClass : "x-form-invalid",
38580     /**
38581      * @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")
38582      */
38583     invalidText : "The value in this field is invalid",
38584     /**
38585      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38586      */
38587     focusClass : "x-form-focus",
38588     /**
38589      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38590       automatic validation (defaults to "keyup").
38591      */
38592     validationEvent : "keyup",
38593     /**
38594      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38595      */
38596     validateOnBlur : true,
38597     /**
38598      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38599      */
38600     validationDelay : 250,
38601     /**
38602      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38603      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38604      */
38605     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38606     /**
38607      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38608      */
38609     fieldClass : "x-form-field",
38610     /**
38611      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38612      *<pre>
38613 Value         Description
38614 -----------   ----------------------------------------------------------------------
38615 qtip          Display a quick tip when the user hovers over the field
38616 title         Display a default browser title attribute popup
38617 under         Add a block div beneath the field containing the error text
38618 side          Add an error icon to the right of the field with a popup on hover
38619 [element id]  Add the error text directly to the innerHTML of the specified element
38620 </pre>
38621      */
38622     msgTarget : 'qtip',
38623     /**
38624      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38625      */
38626     msgFx : 'normal',
38627
38628     /**
38629      * @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.
38630      */
38631     readOnly : false,
38632
38633     /**
38634      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38635      */
38636     disabled : false,
38637
38638     /**
38639      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38640      */
38641     inputType : undefined,
38642     
38643     /**
38644      * @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).
38645          */
38646         tabIndex : undefined,
38647         
38648     // private
38649     isFormField : true,
38650
38651     // private
38652     hasFocus : false,
38653     /**
38654      * @property {Roo.Element} fieldEl
38655      * Element Containing the rendered Field (with label etc.)
38656      */
38657     /**
38658      * @cfg {Mixed} value A value to initialize this field with.
38659      */
38660     value : undefined,
38661
38662     /**
38663      * @cfg {String} name The field's HTML name attribute.
38664      */
38665     /**
38666      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38667      */
38668     // private
38669     loadedValue : false,
38670      
38671      
38672         // private ??
38673         initComponent : function(){
38674         Roo.form.Field.superclass.initComponent.call(this);
38675         this.addEvents({
38676             /**
38677              * @event focus
38678              * Fires when this field receives input focus.
38679              * @param {Roo.form.Field} this
38680              */
38681             focus : true,
38682             /**
38683              * @event blur
38684              * Fires when this field loses input focus.
38685              * @param {Roo.form.Field} this
38686              */
38687             blur : true,
38688             /**
38689              * @event specialkey
38690              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38691              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38692              * @param {Roo.form.Field} this
38693              * @param {Roo.EventObject} e The event object
38694              */
38695             specialkey : true,
38696             /**
38697              * @event change
38698              * Fires just before the field blurs if the field value has changed.
38699              * @param {Roo.form.Field} this
38700              * @param {Mixed} newValue The new value
38701              * @param {Mixed} oldValue The original value
38702              */
38703             change : true,
38704             /**
38705              * @event invalid
38706              * Fires after the field has been marked as invalid.
38707              * @param {Roo.form.Field} this
38708              * @param {String} msg The validation message
38709              */
38710             invalid : true,
38711             /**
38712              * @event valid
38713              * Fires after the field has been validated with no errors.
38714              * @param {Roo.form.Field} this
38715              */
38716             valid : true,
38717              /**
38718              * @event keyup
38719              * Fires after the key up
38720              * @param {Roo.form.Field} this
38721              * @param {Roo.EventObject}  e The event Object
38722              */
38723             keyup : true
38724         });
38725     },
38726
38727     /**
38728      * Returns the name attribute of the field if available
38729      * @return {String} name The field name
38730      */
38731     getName: function(){
38732          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38733     },
38734
38735     // private
38736     onRender : function(ct, position){
38737         Roo.form.Field.superclass.onRender.call(this, ct, position);
38738         if(!this.el){
38739             var cfg = this.getAutoCreate();
38740             if(!cfg.name){
38741                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38742             }
38743             if (!cfg.name.length) {
38744                 delete cfg.name;
38745             }
38746             if(this.inputType){
38747                 cfg.type = this.inputType;
38748             }
38749             this.el = ct.createChild(cfg, position);
38750         }
38751         var type = this.el.dom.type;
38752         if(type){
38753             if(type == 'password'){
38754                 type = 'text';
38755             }
38756             this.el.addClass('x-form-'+type);
38757         }
38758         if(this.readOnly){
38759             this.el.dom.readOnly = true;
38760         }
38761         if(this.tabIndex !== undefined){
38762             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38763         }
38764
38765         this.el.addClass([this.fieldClass, this.cls]);
38766         this.initValue();
38767     },
38768
38769     /**
38770      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38771      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38772      * @return {Roo.form.Field} this
38773      */
38774     applyTo : function(target){
38775         this.allowDomMove = false;
38776         this.el = Roo.get(target);
38777         this.render(this.el.dom.parentNode);
38778         return this;
38779     },
38780
38781     // private
38782     initValue : function(){
38783         if(this.value !== undefined){
38784             this.setValue(this.value);
38785         }else if(this.el.dom.value.length > 0){
38786             this.setValue(this.el.dom.value);
38787         }
38788     },
38789
38790     /**
38791      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38792      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38793      */
38794     isDirty : function() {
38795         if(this.disabled) {
38796             return false;
38797         }
38798         return String(this.getValue()) !== String(this.originalValue);
38799     },
38800
38801     /**
38802      * stores the current value in loadedValue
38803      */
38804     resetHasChanged : function()
38805     {
38806         this.loadedValue = String(this.getValue());
38807     },
38808     /**
38809      * checks the current value against the 'loaded' value.
38810      * Note - will return false if 'resetHasChanged' has not been called first.
38811      */
38812     hasChanged : function()
38813     {
38814         if(this.disabled || this.readOnly) {
38815             return false;
38816         }
38817         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38818     },
38819     
38820     
38821     
38822     // private
38823     afterRender : function(){
38824         Roo.form.Field.superclass.afterRender.call(this);
38825         this.initEvents();
38826     },
38827
38828     // private
38829     fireKey : function(e){
38830         //Roo.log('field ' + e.getKey());
38831         if(e.isNavKeyPress()){
38832             this.fireEvent("specialkey", this, e);
38833         }
38834     },
38835
38836     /**
38837      * Resets the current field value to the originally loaded value and clears any validation messages
38838      */
38839     reset : function(){
38840         this.setValue(this.resetValue);
38841         this.clearInvalid();
38842     },
38843
38844     // private
38845     initEvents : function(){
38846         // safari killled keypress - so keydown is now used..
38847         this.el.on("keydown" , this.fireKey,  this);
38848         this.el.on("focus", this.onFocus,  this);
38849         this.el.on("blur", this.onBlur,  this);
38850         this.el.relayEvent('keyup', this);
38851
38852         // reference to original value for reset
38853         this.originalValue = this.getValue();
38854         this.resetValue =  this.getValue();
38855     },
38856
38857     // private
38858     onFocus : function(){
38859         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38860             this.el.addClass(this.focusClass);
38861         }
38862         if(!this.hasFocus){
38863             this.hasFocus = true;
38864             this.startValue = this.getValue();
38865             this.fireEvent("focus", this);
38866         }
38867     },
38868
38869     beforeBlur : Roo.emptyFn,
38870
38871     // private
38872     onBlur : function(){
38873         this.beforeBlur();
38874         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38875             this.el.removeClass(this.focusClass);
38876         }
38877         this.hasFocus = false;
38878         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38879             this.validate();
38880         }
38881         var v = this.getValue();
38882         if(String(v) !== String(this.startValue)){
38883             this.fireEvent('change', this, v, this.startValue);
38884         }
38885         this.fireEvent("blur", this);
38886     },
38887
38888     /**
38889      * Returns whether or not the field value is currently valid
38890      * @param {Boolean} preventMark True to disable marking the field invalid
38891      * @return {Boolean} True if the value is valid, else false
38892      */
38893     isValid : function(preventMark){
38894         if(this.disabled){
38895             return true;
38896         }
38897         var restore = this.preventMark;
38898         this.preventMark = preventMark === true;
38899         var v = this.validateValue(this.processValue(this.getRawValue()));
38900         this.preventMark = restore;
38901         return v;
38902     },
38903
38904     /**
38905      * Validates the field value
38906      * @return {Boolean} True if the value is valid, else false
38907      */
38908     validate : function(){
38909         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38910             this.clearInvalid();
38911             return true;
38912         }
38913         return false;
38914     },
38915
38916     processValue : function(value){
38917         return value;
38918     },
38919
38920     // private
38921     // Subclasses should provide the validation implementation by overriding this
38922     validateValue : function(value){
38923         return true;
38924     },
38925
38926     /**
38927      * Mark this field as invalid
38928      * @param {String} msg The validation message
38929      */
38930     markInvalid : function(msg){
38931         if(!this.rendered || this.preventMark){ // not rendered
38932             return;
38933         }
38934         
38935         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38936         
38937         obj.el.addClass(this.invalidClass);
38938         msg = msg || this.invalidText;
38939         switch(this.msgTarget){
38940             case 'qtip':
38941                 obj.el.dom.qtip = msg;
38942                 obj.el.dom.qclass = 'x-form-invalid-tip';
38943                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38944                     Roo.QuickTips.enable();
38945                 }
38946                 break;
38947             case 'title':
38948                 this.el.dom.title = msg;
38949                 break;
38950             case 'under':
38951                 if(!this.errorEl){
38952                     var elp = this.el.findParent('.x-form-element', 5, true);
38953                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38954                     this.errorEl.setWidth(elp.getWidth(true)-20);
38955                 }
38956                 this.errorEl.update(msg);
38957                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38958                 break;
38959             case 'side':
38960                 if(!this.errorIcon){
38961                     var elp = this.el.findParent('.x-form-element', 5, true);
38962                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38963                 }
38964                 this.alignErrorIcon();
38965                 this.errorIcon.dom.qtip = msg;
38966                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38967                 this.errorIcon.show();
38968                 this.on('resize', this.alignErrorIcon, this);
38969                 break;
38970             default:
38971                 var t = Roo.getDom(this.msgTarget);
38972                 t.innerHTML = msg;
38973                 t.style.display = this.msgDisplay;
38974                 break;
38975         }
38976         this.fireEvent('invalid', this, msg);
38977     },
38978
38979     // private
38980     alignErrorIcon : function(){
38981         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38982     },
38983
38984     /**
38985      * Clear any invalid styles/messages for this field
38986      */
38987     clearInvalid : function(){
38988         if(!this.rendered || this.preventMark){ // not rendered
38989             return;
38990         }
38991         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38992         
38993         obj.el.removeClass(this.invalidClass);
38994         switch(this.msgTarget){
38995             case 'qtip':
38996                 obj.el.dom.qtip = '';
38997                 break;
38998             case 'title':
38999                 this.el.dom.title = '';
39000                 break;
39001             case 'under':
39002                 if(this.errorEl){
39003                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39004                 }
39005                 break;
39006             case 'side':
39007                 if(this.errorIcon){
39008                     this.errorIcon.dom.qtip = '';
39009                     this.errorIcon.hide();
39010                     this.un('resize', this.alignErrorIcon, this);
39011                 }
39012                 break;
39013             default:
39014                 var t = Roo.getDom(this.msgTarget);
39015                 t.innerHTML = '';
39016                 t.style.display = 'none';
39017                 break;
39018         }
39019         this.fireEvent('valid', this);
39020     },
39021
39022     /**
39023      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39024      * @return {Mixed} value The field value
39025      */
39026     getRawValue : function(){
39027         var v = this.el.getValue();
39028         
39029         return v;
39030     },
39031
39032     /**
39033      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39034      * @return {Mixed} value The field value
39035      */
39036     getValue : function(){
39037         var v = this.el.getValue();
39038          
39039         return v;
39040     },
39041
39042     /**
39043      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39044      * @param {Mixed} value The value to set
39045      */
39046     setRawValue : function(v){
39047         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39048     },
39049
39050     /**
39051      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39052      * @param {Mixed} value The value to set
39053      */
39054     setValue : function(v){
39055         this.value = v;
39056         if(this.rendered){
39057             this.el.dom.value = (v === null || v === undefined ? '' : v);
39058              this.validate();
39059         }
39060     },
39061
39062     adjustSize : function(w, h){
39063         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39064         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39065         return s;
39066     },
39067
39068     adjustWidth : function(tag, w){
39069         tag = tag.toLowerCase();
39070         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39071             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39072                 if(tag == 'input'){
39073                     return w + 2;
39074                 }
39075                 if(tag == 'textarea'){
39076                     return w-2;
39077                 }
39078             }else if(Roo.isOpera){
39079                 if(tag == 'input'){
39080                     return w + 2;
39081                 }
39082                 if(tag == 'textarea'){
39083                     return w-2;
39084                 }
39085             }
39086         }
39087         return w;
39088     }
39089 });
39090
39091
39092 // anything other than normal should be considered experimental
39093 Roo.form.Field.msgFx = {
39094     normal : {
39095         show: function(msgEl, f){
39096             msgEl.setDisplayed('block');
39097         },
39098
39099         hide : function(msgEl, f){
39100             msgEl.setDisplayed(false).update('');
39101         }
39102     },
39103
39104     slide : {
39105         show: function(msgEl, f){
39106             msgEl.slideIn('t', {stopFx:true});
39107         },
39108
39109         hide : function(msgEl, f){
39110             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39111         }
39112     },
39113
39114     slideRight : {
39115         show: function(msgEl, f){
39116             msgEl.fixDisplay();
39117             msgEl.alignTo(f.el, 'tl-tr');
39118             msgEl.slideIn('l', {stopFx:true});
39119         },
39120
39121         hide : function(msgEl, f){
39122             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39123         }
39124     }
39125 };/*
39126  * Based on:
39127  * Ext JS Library 1.1.1
39128  * Copyright(c) 2006-2007, Ext JS, LLC.
39129  *
39130  * Originally Released Under LGPL - original licence link has changed is not relivant.
39131  *
39132  * Fork - LGPL
39133  * <script type="text/javascript">
39134  */
39135  
39136
39137 /**
39138  * @class Roo.form.TextField
39139  * @extends Roo.form.Field
39140  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39141  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39142  * @constructor
39143  * Creates a new TextField
39144  * @param {Object} config Configuration options
39145  */
39146 Roo.form.TextField = function(config){
39147     Roo.form.TextField.superclass.constructor.call(this, config);
39148     this.addEvents({
39149         /**
39150          * @event autosize
39151          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39152          * according to the default logic, but this event provides a hook for the developer to apply additional
39153          * logic at runtime to resize the field if needed.
39154              * @param {Roo.form.Field} this This text field
39155              * @param {Number} width The new field width
39156              */
39157         autosize : true
39158     });
39159 };
39160
39161 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39162     /**
39163      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39164      */
39165     grow : false,
39166     /**
39167      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39168      */
39169     growMin : 30,
39170     /**
39171      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39172      */
39173     growMax : 800,
39174     /**
39175      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39176      */
39177     vtype : null,
39178     /**
39179      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39180      */
39181     maskRe : null,
39182     /**
39183      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39184      */
39185     disableKeyFilter : false,
39186     /**
39187      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39188      */
39189     allowBlank : true,
39190     /**
39191      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39192      */
39193     minLength : 0,
39194     /**
39195      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39196      */
39197     maxLength : Number.MAX_VALUE,
39198     /**
39199      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39200      */
39201     minLengthText : "The minimum length for this field is {0}",
39202     /**
39203      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39204      */
39205     maxLengthText : "The maximum length for this field is {0}",
39206     /**
39207      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39208      */
39209     selectOnFocus : false,
39210     /**
39211      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39212      */
39213     blankText : "This field is required",
39214     /**
39215      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39216      * If available, this function will be called only after the basic validators all return true, and will be passed the
39217      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39218      */
39219     validator : null,
39220     /**
39221      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39222      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39223      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39224      */
39225     regex : null,
39226     /**
39227      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39228      */
39229     regexText : "",
39230     /**
39231      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39232      */
39233     emptyText : null,
39234    
39235
39236     // private
39237     initEvents : function()
39238     {
39239         if (this.emptyText) {
39240             this.el.attr('placeholder', this.emptyText);
39241         }
39242         
39243         Roo.form.TextField.superclass.initEvents.call(this);
39244         if(this.validationEvent == 'keyup'){
39245             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39246             this.el.on('keyup', this.filterValidation, this);
39247         }
39248         else if(this.validationEvent !== false){
39249             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39250         }
39251         
39252         if(this.selectOnFocus){
39253             this.on("focus", this.preFocus, this);
39254             
39255         }
39256         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39257             this.el.on("keypress", this.filterKeys, this);
39258         }
39259         if(this.grow){
39260             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39261             this.el.on("click", this.autoSize,  this);
39262         }
39263         if(this.el.is('input[type=password]') && Roo.isSafari){
39264             this.el.on('keydown', this.SafariOnKeyDown, this);
39265         }
39266     },
39267
39268     processValue : function(value){
39269         if(this.stripCharsRe){
39270             var newValue = value.replace(this.stripCharsRe, '');
39271             if(newValue !== value){
39272                 this.setRawValue(newValue);
39273                 return newValue;
39274             }
39275         }
39276         return value;
39277     },
39278
39279     filterValidation : function(e){
39280         if(!e.isNavKeyPress()){
39281             this.validationTask.delay(this.validationDelay);
39282         }
39283     },
39284
39285     // private
39286     onKeyUp : function(e){
39287         if(!e.isNavKeyPress()){
39288             this.autoSize();
39289         }
39290     },
39291
39292     /**
39293      * Resets the current field value to the originally-loaded value and clears any validation messages.
39294      *  
39295      */
39296     reset : function(){
39297         Roo.form.TextField.superclass.reset.call(this);
39298        
39299     },
39300
39301     
39302     // private
39303     preFocus : function(){
39304         
39305         if(this.selectOnFocus){
39306             this.el.dom.select();
39307         }
39308     },
39309
39310     
39311     // private
39312     filterKeys : function(e){
39313         var k = e.getKey();
39314         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39315             return;
39316         }
39317         var c = e.getCharCode(), cc = String.fromCharCode(c);
39318         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39319             return;
39320         }
39321         if(!this.maskRe.test(cc)){
39322             e.stopEvent();
39323         }
39324     },
39325
39326     setValue : function(v){
39327         
39328         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39329         
39330         this.autoSize();
39331     },
39332
39333     /**
39334      * Validates a value according to the field's validation rules and marks the field as invalid
39335      * if the validation fails
39336      * @param {Mixed} value The value to validate
39337      * @return {Boolean} True if the value is valid, else false
39338      */
39339     validateValue : function(value){
39340         if(value.length < 1)  { // if it's blank
39341              if(this.allowBlank){
39342                 this.clearInvalid();
39343                 return true;
39344              }else{
39345                 this.markInvalid(this.blankText);
39346                 return false;
39347              }
39348         }
39349         if(value.length < this.minLength){
39350             this.markInvalid(String.format(this.minLengthText, this.minLength));
39351             return false;
39352         }
39353         if(value.length > this.maxLength){
39354             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39355             return false;
39356         }
39357         if(this.vtype){
39358             var vt = Roo.form.VTypes;
39359             if(!vt[this.vtype](value, this)){
39360                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39361                 return false;
39362             }
39363         }
39364         if(typeof this.validator == "function"){
39365             var msg = this.validator(value);
39366             if(msg !== true){
39367                 this.markInvalid(msg);
39368                 return false;
39369             }
39370         }
39371         if(this.regex && !this.regex.test(value)){
39372             this.markInvalid(this.regexText);
39373             return false;
39374         }
39375         return true;
39376     },
39377
39378     /**
39379      * Selects text in this field
39380      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39381      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39382      */
39383     selectText : function(start, end){
39384         var v = this.getRawValue();
39385         if(v.length > 0){
39386             start = start === undefined ? 0 : start;
39387             end = end === undefined ? v.length : end;
39388             var d = this.el.dom;
39389             if(d.setSelectionRange){
39390                 d.setSelectionRange(start, end);
39391             }else if(d.createTextRange){
39392                 var range = d.createTextRange();
39393                 range.moveStart("character", start);
39394                 range.moveEnd("character", v.length-end);
39395                 range.select();
39396             }
39397         }
39398     },
39399
39400     /**
39401      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39402      * This only takes effect if grow = true, and fires the autosize event.
39403      */
39404     autoSize : function(){
39405         if(!this.grow || !this.rendered){
39406             return;
39407         }
39408         if(!this.metrics){
39409             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39410         }
39411         var el = this.el;
39412         var v = el.dom.value;
39413         var d = document.createElement('div');
39414         d.appendChild(document.createTextNode(v));
39415         v = d.innerHTML;
39416         d = null;
39417         v += "&#160;";
39418         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39419         this.el.setWidth(w);
39420         this.fireEvent("autosize", this, w);
39421     },
39422     
39423     // private
39424     SafariOnKeyDown : function(event)
39425     {
39426         // this is a workaround for a password hang bug on chrome/ webkit.
39427         
39428         var isSelectAll = false;
39429         
39430         if(this.el.dom.selectionEnd > 0){
39431             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39432         }
39433         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39434             event.preventDefault();
39435             this.setValue('');
39436             return;
39437         }
39438         
39439         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39440             
39441             event.preventDefault();
39442             // this is very hacky as keydown always get's upper case.
39443             
39444             var cc = String.fromCharCode(event.getCharCode());
39445             
39446             
39447             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39448             
39449         }
39450         
39451         
39452     }
39453 });/*
39454  * Based on:
39455  * Ext JS Library 1.1.1
39456  * Copyright(c) 2006-2007, Ext JS, LLC.
39457  *
39458  * Originally Released Under LGPL - original licence link has changed is not relivant.
39459  *
39460  * Fork - LGPL
39461  * <script type="text/javascript">
39462  */
39463  
39464 /**
39465  * @class Roo.form.Hidden
39466  * @extends Roo.form.TextField
39467  * Simple Hidden element used on forms 
39468  * 
39469  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39470  * 
39471  * @constructor
39472  * Creates a new Hidden form element.
39473  * @param {Object} config Configuration options
39474  */
39475
39476
39477
39478 // easy hidden field...
39479 Roo.form.Hidden = function(config){
39480     Roo.form.Hidden.superclass.constructor.call(this, config);
39481 };
39482   
39483 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39484     fieldLabel:      '',
39485     inputType:      'hidden',
39486     width:          50,
39487     allowBlank:     true,
39488     labelSeparator: '',
39489     hidden:         true,
39490     itemCls :       'x-form-item-display-none'
39491
39492
39493 });
39494
39495
39496 /*
39497  * Based on:
39498  * Ext JS Library 1.1.1
39499  * Copyright(c) 2006-2007, Ext JS, LLC.
39500  *
39501  * Originally Released Under LGPL - original licence link has changed is not relivant.
39502  *
39503  * Fork - LGPL
39504  * <script type="text/javascript">
39505  */
39506  
39507 /**
39508  * @class Roo.form.TriggerField
39509  * @extends Roo.form.TextField
39510  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39511  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39512  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39513  * for which you can provide a custom implementation.  For example:
39514  * <pre><code>
39515 var trigger = new Roo.form.TriggerField();
39516 trigger.onTriggerClick = myTriggerFn;
39517 trigger.applyTo('my-field');
39518 </code></pre>
39519  *
39520  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39521  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39522  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39523  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39524  * @constructor
39525  * Create a new TriggerField.
39526  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39527  * to the base TextField)
39528  */
39529 Roo.form.TriggerField = function(config){
39530     this.mimicing = false;
39531     Roo.form.TriggerField.superclass.constructor.call(this, config);
39532 };
39533
39534 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39535     /**
39536      * @cfg {String} triggerClass A CSS class to apply to the trigger
39537      */
39538     /**
39539      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39540      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39541      */
39542     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39543     /**
39544      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39545      */
39546     hideTrigger:false,
39547
39548     /** @cfg {Boolean} grow @hide */
39549     /** @cfg {Number} growMin @hide */
39550     /** @cfg {Number} growMax @hide */
39551
39552     /**
39553      * @hide 
39554      * @method
39555      */
39556     autoSize: Roo.emptyFn,
39557     // private
39558     monitorTab : true,
39559     // private
39560     deferHeight : true,
39561
39562     
39563     actionMode : 'wrap',
39564     // private
39565     onResize : function(w, h){
39566         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39567         if(typeof w == 'number'){
39568             var x = w - this.trigger.getWidth();
39569             this.el.setWidth(this.adjustWidth('input', x));
39570             this.trigger.setStyle('left', x+'px');
39571         }
39572     },
39573
39574     // private
39575     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39576
39577     // private
39578     getResizeEl : function(){
39579         return this.wrap;
39580     },
39581
39582     // private
39583     getPositionEl : function(){
39584         return this.wrap;
39585     },
39586
39587     // private
39588     alignErrorIcon : function(){
39589         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39590     },
39591
39592     // private
39593     onRender : function(ct, position){
39594         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39595         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39596         this.trigger = this.wrap.createChild(this.triggerConfig ||
39597                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39598         if(this.hideTrigger){
39599             this.trigger.setDisplayed(false);
39600         }
39601         this.initTrigger();
39602         if(!this.width){
39603             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39604         }
39605     },
39606
39607     // private
39608     initTrigger : function(){
39609         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39610         this.trigger.addClassOnOver('x-form-trigger-over');
39611         this.trigger.addClassOnClick('x-form-trigger-click');
39612     },
39613
39614     // private
39615     onDestroy : function(){
39616         if(this.trigger){
39617             this.trigger.removeAllListeners();
39618             this.trigger.remove();
39619         }
39620         if(this.wrap){
39621             this.wrap.remove();
39622         }
39623         Roo.form.TriggerField.superclass.onDestroy.call(this);
39624     },
39625
39626     // private
39627     onFocus : function(){
39628         Roo.form.TriggerField.superclass.onFocus.call(this);
39629         if(!this.mimicing){
39630             this.wrap.addClass('x-trigger-wrap-focus');
39631             this.mimicing = true;
39632             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39633             if(this.monitorTab){
39634                 this.el.on("keydown", this.checkTab, this);
39635             }
39636         }
39637     },
39638
39639     // private
39640     checkTab : function(e){
39641         if(e.getKey() == e.TAB){
39642             this.triggerBlur();
39643         }
39644     },
39645
39646     // private
39647     onBlur : function(){
39648         // do nothing
39649     },
39650
39651     // private
39652     mimicBlur : function(e, t){
39653         if(!this.wrap.contains(t) && this.validateBlur()){
39654             this.triggerBlur();
39655         }
39656     },
39657
39658     // private
39659     triggerBlur : function(){
39660         this.mimicing = false;
39661         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39662         if(this.monitorTab){
39663             this.el.un("keydown", this.checkTab, this);
39664         }
39665         this.wrap.removeClass('x-trigger-wrap-focus');
39666         Roo.form.TriggerField.superclass.onBlur.call(this);
39667     },
39668
39669     // private
39670     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39671     validateBlur : function(e, t){
39672         return true;
39673     },
39674
39675     // private
39676     onDisable : function(){
39677         Roo.form.TriggerField.superclass.onDisable.call(this);
39678         if(this.wrap){
39679             this.wrap.addClass('x-item-disabled');
39680         }
39681     },
39682
39683     // private
39684     onEnable : function(){
39685         Roo.form.TriggerField.superclass.onEnable.call(this);
39686         if(this.wrap){
39687             this.wrap.removeClass('x-item-disabled');
39688         }
39689     },
39690
39691     // private
39692     onShow : function(){
39693         var ae = this.getActionEl();
39694         
39695         if(ae){
39696             ae.dom.style.display = '';
39697             ae.dom.style.visibility = 'visible';
39698         }
39699     },
39700
39701     // private
39702     
39703     onHide : function(){
39704         var ae = this.getActionEl();
39705         ae.dom.style.display = 'none';
39706     },
39707
39708     /**
39709      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39710      * by an implementing function.
39711      * @method
39712      * @param {EventObject} e
39713      */
39714     onTriggerClick : Roo.emptyFn
39715 });
39716
39717 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39718 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39719 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39720 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39721     initComponent : function(){
39722         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39723
39724         this.triggerConfig = {
39725             tag:'span', cls:'x-form-twin-triggers', cn:[
39726             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39727             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39728         ]};
39729     },
39730
39731     getTrigger : function(index){
39732         return this.triggers[index];
39733     },
39734
39735     initTrigger : function(){
39736         var ts = this.trigger.select('.x-form-trigger', true);
39737         this.wrap.setStyle('overflow', 'hidden');
39738         var triggerField = this;
39739         ts.each(function(t, all, index){
39740             t.hide = function(){
39741                 var w = triggerField.wrap.getWidth();
39742                 this.dom.style.display = 'none';
39743                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39744             };
39745             t.show = function(){
39746                 var w = triggerField.wrap.getWidth();
39747                 this.dom.style.display = '';
39748                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39749             };
39750             var triggerIndex = 'Trigger'+(index+1);
39751
39752             if(this['hide'+triggerIndex]){
39753                 t.dom.style.display = 'none';
39754             }
39755             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39756             t.addClassOnOver('x-form-trigger-over');
39757             t.addClassOnClick('x-form-trigger-click');
39758         }, this);
39759         this.triggers = ts.elements;
39760     },
39761
39762     onTrigger1Click : Roo.emptyFn,
39763     onTrigger2Click : Roo.emptyFn
39764 });/*
39765  * Based on:
39766  * Ext JS Library 1.1.1
39767  * Copyright(c) 2006-2007, Ext JS, LLC.
39768  *
39769  * Originally Released Under LGPL - original licence link has changed is not relivant.
39770  *
39771  * Fork - LGPL
39772  * <script type="text/javascript">
39773  */
39774  
39775 /**
39776  * @class Roo.form.TextArea
39777  * @extends Roo.form.TextField
39778  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39779  * support for auto-sizing.
39780  * @constructor
39781  * Creates a new TextArea
39782  * @param {Object} config Configuration options
39783  */
39784 Roo.form.TextArea = function(config){
39785     Roo.form.TextArea.superclass.constructor.call(this, config);
39786     // these are provided exchanges for backwards compat
39787     // minHeight/maxHeight were replaced by growMin/growMax to be
39788     // compatible with TextField growing config values
39789     if(this.minHeight !== undefined){
39790         this.growMin = this.minHeight;
39791     }
39792     if(this.maxHeight !== undefined){
39793         this.growMax = this.maxHeight;
39794     }
39795 };
39796
39797 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39798     /**
39799      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39800      */
39801     growMin : 60,
39802     /**
39803      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39804      */
39805     growMax: 1000,
39806     /**
39807      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39808      * in the field (equivalent to setting overflow: hidden, defaults to false)
39809      */
39810     preventScrollbars: false,
39811     /**
39812      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39813      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39814      */
39815
39816     // private
39817     onRender : function(ct, position){
39818         if(!this.el){
39819             this.defaultAutoCreate = {
39820                 tag: "textarea",
39821                 style:"width:300px;height:60px;",
39822                 autocomplete: "new-password"
39823             };
39824         }
39825         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39826         if(this.grow){
39827             this.textSizeEl = Roo.DomHelper.append(document.body, {
39828                 tag: "pre", cls: "x-form-grow-sizer"
39829             });
39830             if(this.preventScrollbars){
39831                 this.el.setStyle("overflow", "hidden");
39832             }
39833             this.el.setHeight(this.growMin);
39834         }
39835     },
39836
39837     onDestroy : function(){
39838         if(this.textSizeEl){
39839             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39840         }
39841         Roo.form.TextArea.superclass.onDestroy.call(this);
39842     },
39843
39844     // private
39845     onKeyUp : function(e){
39846         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39847             this.autoSize();
39848         }
39849     },
39850
39851     /**
39852      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39853      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39854      */
39855     autoSize : function(){
39856         if(!this.grow || !this.textSizeEl){
39857             return;
39858         }
39859         var el = this.el;
39860         var v = el.dom.value;
39861         var ts = this.textSizeEl;
39862
39863         ts.innerHTML = '';
39864         ts.appendChild(document.createTextNode(v));
39865         v = ts.innerHTML;
39866
39867         Roo.fly(ts).setWidth(this.el.getWidth());
39868         if(v.length < 1){
39869             v = "&#160;&#160;";
39870         }else{
39871             if(Roo.isIE){
39872                 v = v.replace(/\n/g, '<p>&#160;</p>');
39873             }
39874             v += "&#160;\n&#160;";
39875         }
39876         ts.innerHTML = v;
39877         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39878         if(h != this.lastHeight){
39879             this.lastHeight = h;
39880             this.el.setHeight(h);
39881             this.fireEvent("autosize", this, h);
39882         }
39883     }
39884 });/*
39885  * Based on:
39886  * Ext JS Library 1.1.1
39887  * Copyright(c) 2006-2007, Ext JS, LLC.
39888  *
39889  * Originally Released Under LGPL - original licence link has changed is not relivant.
39890  *
39891  * Fork - LGPL
39892  * <script type="text/javascript">
39893  */
39894  
39895
39896 /**
39897  * @class Roo.form.NumberField
39898  * @extends Roo.form.TextField
39899  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39900  * @constructor
39901  * Creates a new NumberField
39902  * @param {Object} config Configuration options
39903  */
39904 Roo.form.NumberField = function(config){
39905     Roo.form.NumberField.superclass.constructor.call(this, config);
39906 };
39907
39908 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39909     /**
39910      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39911      */
39912     fieldClass: "x-form-field x-form-num-field",
39913     /**
39914      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39915      */
39916     allowDecimals : true,
39917     /**
39918      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39919      */
39920     decimalSeparator : ".",
39921     /**
39922      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39923      */
39924     decimalPrecision : 2,
39925     /**
39926      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39927      */
39928     allowNegative : true,
39929     /**
39930      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39931      */
39932     minValue : Number.NEGATIVE_INFINITY,
39933     /**
39934      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39935      */
39936     maxValue : Number.MAX_VALUE,
39937     /**
39938      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39939      */
39940     minText : "The minimum value for this field is {0}",
39941     /**
39942      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39943      */
39944     maxText : "The maximum value for this field is {0}",
39945     /**
39946      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39947      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39948      */
39949     nanText : "{0} is not a valid number",
39950
39951     // private
39952     initEvents : function(){
39953         Roo.form.NumberField.superclass.initEvents.call(this);
39954         var allowed = "0123456789";
39955         if(this.allowDecimals){
39956             allowed += this.decimalSeparator;
39957         }
39958         if(this.allowNegative){
39959             allowed += "-";
39960         }
39961         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39962         var keyPress = function(e){
39963             var k = e.getKey();
39964             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39965                 return;
39966             }
39967             var c = e.getCharCode();
39968             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39969                 e.stopEvent();
39970             }
39971         };
39972         this.el.on("keypress", keyPress, this);
39973     },
39974
39975     // private
39976     validateValue : function(value){
39977         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39978             return false;
39979         }
39980         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39981              return true;
39982         }
39983         var num = this.parseValue(value);
39984         if(isNaN(num)){
39985             this.markInvalid(String.format(this.nanText, value));
39986             return false;
39987         }
39988         if(num < this.minValue){
39989             this.markInvalid(String.format(this.minText, this.minValue));
39990             return false;
39991         }
39992         if(num > this.maxValue){
39993             this.markInvalid(String.format(this.maxText, this.maxValue));
39994             return false;
39995         }
39996         return true;
39997     },
39998
39999     getValue : function(){
40000         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40001     },
40002
40003     // private
40004     parseValue : function(value){
40005         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40006         return isNaN(value) ? '' : value;
40007     },
40008
40009     // private
40010     fixPrecision : function(value){
40011         var nan = isNaN(value);
40012         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40013             return nan ? '' : value;
40014         }
40015         return parseFloat(value).toFixed(this.decimalPrecision);
40016     },
40017
40018     setValue : function(v){
40019         v = this.fixPrecision(v);
40020         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40021     },
40022
40023     // private
40024     decimalPrecisionFcn : function(v){
40025         return Math.floor(v);
40026     },
40027
40028     beforeBlur : function(){
40029         var v = this.parseValue(this.getRawValue());
40030         if(v){
40031             this.setValue(v);
40032         }
40033     }
40034 });/*
40035  * Based on:
40036  * Ext JS Library 1.1.1
40037  * Copyright(c) 2006-2007, Ext JS, LLC.
40038  *
40039  * Originally Released Under LGPL - original licence link has changed is not relivant.
40040  *
40041  * Fork - LGPL
40042  * <script type="text/javascript">
40043  */
40044  
40045 /**
40046  * @class Roo.form.DateField
40047  * @extends Roo.form.TriggerField
40048  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40049 * @constructor
40050 * Create a new DateField
40051 * @param {Object} config
40052  */
40053 Roo.form.DateField = function(config){
40054     Roo.form.DateField.superclass.constructor.call(this, config);
40055     
40056       this.addEvents({
40057          
40058         /**
40059          * @event select
40060          * Fires when a date is selected
40061              * @param {Roo.form.DateField} combo This combo box
40062              * @param {Date} date The date selected
40063              */
40064         'select' : true
40065          
40066     });
40067     
40068     
40069     if(typeof this.minValue == "string") {
40070         this.minValue = this.parseDate(this.minValue);
40071     }
40072     if(typeof this.maxValue == "string") {
40073         this.maxValue = this.parseDate(this.maxValue);
40074     }
40075     this.ddMatch = null;
40076     if(this.disabledDates){
40077         var dd = this.disabledDates;
40078         var re = "(?:";
40079         for(var i = 0; i < dd.length; i++){
40080             re += dd[i];
40081             if(i != dd.length-1) {
40082                 re += "|";
40083             }
40084         }
40085         this.ddMatch = new RegExp(re + ")");
40086     }
40087 };
40088
40089 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40090     /**
40091      * @cfg {String} format
40092      * The default date format string which can be overriden for localization support.  The format must be
40093      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40094      */
40095     format : "m/d/y",
40096     /**
40097      * @cfg {String} altFormats
40098      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40099      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40100      */
40101     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40102     /**
40103      * @cfg {Array} disabledDays
40104      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40105      */
40106     disabledDays : null,
40107     /**
40108      * @cfg {String} disabledDaysText
40109      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40110      */
40111     disabledDaysText : "Disabled",
40112     /**
40113      * @cfg {Array} disabledDates
40114      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40115      * expression so they are very powerful. Some examples:
40116      * <ul>
40117      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40118      * <li>["03/08", "09/16"] would disable those days for every year</li>
40119      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40120      * <li>["03/../2006"] would disable every day in March 2006</li>
40121      * <li>["^03"] would disable every day in every March</li>
40122      * </ul>
40123      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40124      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40125      */
40126     disabledDates : null,
40127     /**
40128      * @cfg {String} disabledDatesText
40129      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40130      */
40131     disabledDatesText : "Disabled",
40132     /**
40133      * @cfg {Date/String} minValue
40134      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40135      * valid format (defaults to null).
40136      */
40137     minValue : null,
40138     /**
40139      * @cfg {Date/String} maxValue
40140      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40141      * valid format (defaults to null).
40142      */
40143     maxValue : null,
40144     /**
40145      * @cfg {String} minText
40146      * The error text to display when the date in the cell is before minValue (defaults to
40147      * 'The date in this field must be after {minValue}').
40148      */
40149     minText : "The date in this field must be equal to or after {0}",
40150     /**
40151      * @cfg {String} maxText
40152      * The error text to display when the date in the cell is after maxValue (defaults to
40153      * 'The date in this field must be before {maxValue}').
40154      */
40155     maxText : "The date in this field must be equal to or before {0}",
40156     /**
40157      * @cfg {String} invalidText
40158      * The error text to display when the date in the field is invalid (defaults to
40159      * '{value} is not a valid date - it must be in the format {format}').
40160      */
40161     invalidText : "{0} is not a valid date - it must be in the format {1}",
40162     /**
40163      * @cfg {String} triggerClass
40164      * An additional CSS class used to style the trigger button.  The trigger will always get the
40165      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40166      * which displays a calendar icon).
40167      */
40168     triggerClass : 'x-form-date-trigger',
40169     
40170
40171     /**
40172      * @cfg {Boolean} useIso
40173      * if enabled, then the date field will use a hidden field to store the 
40174      * real value as iso formated date. default (false)
40175      */ 
40176     useIso : false,
40177     /**
40178      * @cfg {String/Object} autoCreate
40179      * A DomHelper element spec, or true for a default element spec (defaults to
40180      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40181      */ 
40182     // private
40183     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40184     
40185     // private
40186     hiddenField: false,
40187     
40188     onRender : function(ct, position)
40189     {
40190         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40191         if (this.useIso) {
40192             //this.el.dom.removeAttribute('name'); 
40193             Roo.log("Changing name?");
40194             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40195             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40196                     'before', true);
40197             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40198             // prevent input submission
40199             this.hiddenName = this.name;
40200         }
40201             
40202             
40203     },
40204     
40205     // private
40206     validateValue : function(value)
40207     {
40208         value = this.formatDate(value);
40209         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40210             Roo.log('super failed');
40211             return false;
40212         }
40213         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40214              return true;
40215         }
40216         var svalue = value;
40217         value = this.parseDate(value);
40218         if(!value){
40219             Roo.log('parse date failed' + svalue);
40220             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40221             return false;
40222         }
40223         var time = value.getTime();
40224         if(this.minValue && time < this.minValue.getTime()){
40225             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40226             return false;
40227         }
40228         if(this.maxValue && time > this.maxValue.getTime()){
40229             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40230             return false;
40231         }
40232         if(this.disabledDays){
40233             var day = value.getDay();
40234             for(var i = 0; i < this.disabledDays.length; i++) {
40235                 if(day === this.disabledDays[i]){
40236                     this.markInvalid(this.disabledDaysText);
40237                     return false;
40238                 }
40239             }
40240         }
40241         var fvalue = this.formatDate(value);
40242         if(this.ddMatch && this.ddMatch.test(fvalue)){
40243             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40244             return false;
40245         }
40246         return true;
40247     },
40248
40249     // private
40250     // Provides logic to override the default TriggerField.validateBlur which just returns true
40251     validateBlur : function(){
40252         return !this.menu || !this.menu.isVisible();
40253     },
40254     
40255     getName: function()
40256     {
40257         // returns hidden if it's set..
40258         if (!this.rendered) {return ''};
40259         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40260         
40261     },
40262
40263     /**
40264      * Returns the current date value of the date field.
40265      * @return {Date} The date value
40266      */
40267     getValue : function(){
40268         
40269         return  this.hiddenField ?
40270                 this.hiddenField.value :
40271                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40272     },
40273
40274     /**
40275      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40276      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40277      * (the default format used is "m/d/y").
40278      * <br />Usage:
40279      * <pre><code>
40280 //All of these calls set the same date value (May 4, 2006)
40281
40282 //Pass a date object:
40283 var dt = new Date('5/4/06');
40284 dateField.setValue(dt);
40285
40286 //Pass a date string (default format):
40287 dateField.setValue('5/4/06');
40288
40289 //Pass a date string (custom format):
40290 dateField.format = 'Y-m-d';
40291 dateField.setValue('2006-5-4');
40292 </code></pre>
40293      * @param {String/Date} date The date or valid date string
40294      */
40295     setValue : function(date){
40296         if (this.hiddenField) {
40297             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40298         }
40299         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40300         // make sure the value field is always stored as a date..
40301         this.value = this.parseDate(date);
40302         
40303         
40304     },
40305
40306     // private
40307     parseDate : function(value){
40308         if(!value || value instanceof Date){
40309             return value;
40310         }
40311         var v = Date.parseDate(value, this.format);
40312          if (!v && this.useIso) {
40313             v = Date.parseDate(value, 'Y-m-d');
40314         }
40315         if(!v && this.altFormats){
40316             if(!this.altFormatsArray){
40317                 this.altFormatsArray = this.altFormats.split("|");
40318             }
40319             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40320                 v = Date.parseDate(value, this.altFormatsArray[i]);
40321             }
40322         }
40323         return v;
40324     },
40325
40326     // private
40327     formatDate : function(date, fmt){
40328         return (!date || !(date instanceof Date)) ?
40329                date : date.dateFormat(fmt || this.format);
40330     },
40331
40332     // private
40333     menuListeners : {
40334         select: function(m, d){
40335             
40336             this.setValue(d);
40337             this.fireEvent('select', this, d);
40338         },
40339         show : function(){ // retain focus styling
40340             this.onFocus();
40341         },
40342         hide : function(){
40343             this.focus.defer(10, this);
40344             var ml = this.menuListeners;
40345             this.menu.un("select", ml.select,  this);
40346             this.menu.un("show", ml.show,  this);
40347             this.menu.un("hide", ml.hide,  this);
40348         }
40349     },
40350
40351     // private
40352     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40353     onTriggerClick : function(){
40354         if(this.disabled){
40355             return;
40356         }
40357         if(this.menu == null){
40358             this.menu = new Roo.menu.DateMenu();
40359         }
40360         Roo.apply(this.menu.picker,  {
40361             showClear: this.allowBlank,
40362             minDate : this.minValue,
40363             maxDate : this.maxValue,
40364             disabledDatesRE : this.ddMatch,
40365             disabledDatesText : this.disabledDatesText,
40366             disabledDays : this.disabledDays,
40367             disabledDaysText : this.disabledDaysText,
40368             format : this.useIso ? 'Y-m-d' : this.format,
40369             minText : String.format(this.minText, this.formatDate(this.minValue)),
40370             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40371         });
40372         this.menu.on(Roo.apply({}, this.menuListeners, {
40373             scope:this
40374         }));
40375         this.menu.picker.setValue(this.getValue() || new Date());
40376         this.menu.show(this.el, "tl-bl?");
40377     },
40378
40379     beforeBlur : function(){
40380         var v = this.parseDate(this.getRawValue());
40381         if(v){
40382             this.setValue(v);
40383         }
40384     },
40385
40386     /*@
40387      * overide
40388      * 
40389      */
40390     isDirty : function() {
40391         if(this.disabled) {
40392             return false;
40393         }
40394         
40395         if(typeof(this.startValue) === 'undefined'){
40396             return false;
40397         }
40398         
40399         return String(this.getValue()) !== String(this.startValue);
40400         
40401     }
40402 });/*
40403  * Based on:
40404  * Ext JS Library 1.1.1
40405  * Copyright(c) 2006-2007, Ext JS, LLC.
40406  *
40407  * Originally Released Under LGPL - original licence link has changed is not relivant.
40408  *
40409  * Fork - LGPL
40410  * <script type="text/javascript">
40411  */
40412  
40413 /**
40414  * @class Roo.form.MonthField
40415  * @extends Roo.form.TriggerField
40416  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40417 * @constructor
40418 * Create a new MonthField
40419 * @param {Object} config
40420  */
40421 Roo.form.MonthField = function(config){
40422     
40423     Roo.form.MonthField.superclass.constructor.call(this, config);
40424     
40425       this.addEvents({
40426          
40427         /**
40428          * @event select
40429          * Fires when a date is selected
40430              * @param {Roo.form.MonthFieeld} combo This combo box
40431              * @param {Date} date The date selected
40432              */
40433         'select' : true
40434          
40435     });
40436     
40437     
40438     if(typeof this.minValue == "string") {
40439         this.minValue = this.parseDate(this.minValue);
40440     }
40441     if(typeof this.maxValue == "string") {
40442         this.maxValue = this.parseDate(this.maxValue);
40443     }
40444     this.ddMatch = null;
40445     if(this.disabledDates){
40446         var dd = this.disabledDates;
40447         var re = "(?:";
40448         for(var i = 0; i < dd.length; i++){
40449             re += dd[i];
40450             if(i != dd.length-1) {
40451                 re += "|";
40452             }
40453         }
40454         this.ddMatch = new RegExp(re + ")");
40455     }
40456 };
40457
40458 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40459     /**
40460      * @cfg {String} format
40461      * The default date format string which can be overriden for localization support.  The format must be
40462      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40463      */
40464     format : "M Y",
40465     /**
40466      * @cfg {String} altFormats
40467      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40468      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40469      */
40470     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40471     /**
40472      * @cfg {Array} disabledDays
40473      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40474      */
40475     disabledDays : [0,1,2,3,4,5,6],
40476     /**
40477      * @cfg {String} disabledDaysText
40478      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40479      */
40480     disabledDaysText : "Disabled",
40481     /**
40482      * @cfg {Array} disabledDates
40483      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40484      * expression so they are very powerful. Some examples:
40485      * <ul>
40486      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40487      * <li>["03/08", "09/16"] would disable those days for every year</li>
40488      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40489      * <li>["03/../2006"] would disable every day in March 2006</li>
40490      * <li>["^03"] would disable every day in every March</li>
40491      * </ul>
40492      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40493      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40494      */
40495     disabledDates : null,
40496     /**
40497      * @cfg {String} disabledDatesText
40498      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40499      */
40500     disabledDatesText : "Disabled",
40501     /**
40502      * @cfg {Date/String} minValue
40503      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40504      * valid format (defaults to null).
40505      */
40506     minValue : null,
40507     /**
40508      * @cfg {Date/String} maxValue
40509      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40510      * valid format (defaults to null).
40511      */
40512     maxValue : null,
40513     /**
40514      * @cfg {String} minText
40515      * The error text to display when the date in the cell is before minValue (defaults to
40516      * 'The date in this field must be after {minValue}').
40517      */
40518     minText : "The date in this field must be equal to or after {0}",
40519     /**
40520      * @cfg {String} maxTextf
40521      * The error text to display when the date in the cell is after maxValue (defaults to
40522      * 'The date in this field must be before {maxValue}').
40523      */
40524     maxText : "The date in this field must be equal to or before {0}",
40525     /**
40526      * @cfg {String} invalidText
40527      * The error text to display when the date in the field is invalid (defaults to
40528      * '{value} is not a valid date - it must be in the format {format}').
40529      */
40530     invalidText : "{0} is not a valid date - it must be in the format {1}",
40531     /**
40532      * @cfg {String} triggerClass
40533      * An additional CSS class used to style the trigger button.  The trigger will always get the
40534      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40535      * which displays a calendar icon).
40536      */
40537     triggerClass : 'x-form-date-trigger',
40538     
40539
40540     /**
40541      * @cfg {Boolean} useIso
40542      * if enabled, then the date field will use a hidden field to store the 
40543      * real value as iso formated date. default (true)
40544      */ 
40545     useIso : true,
40546     /**
40547      * @cfg {String/Object} autoCreate
40548      * A DomHelper element spec, or true for a default element spec (defaults to
40549      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40550      */ 
40551     // private
40552     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40553     
40554     // private
40555     hiddenField: false,
40556     
40557     hideMonthPicker : false,
40558     
40559     onRender : function(ct, position)
40560     {
40561         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40562         if (this.useIso) {
40563             this.el.dom.removeAttribute('name'); 
40564             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40565                     'before', true);
40566             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40567             // prevent input submission
40568             this.hiddenName = this.name;
40569         }
40570             
40571             
40572     },
40573     
40574     // private
40575     validateValue : function(value)
40576     {
40577         value = this.formatDate(value);
40578         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40579             return false;
40580         }
40581         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40582              return true;
40583         }
40584         var svalue = value;
40585         value = this.parseDate(value);
40586         if(!value){
40587             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40588             return false;
40589         }
40590         var time = value.getTime();
40591         if(this.minValue && time < this.minValue.getTime()){
40592             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40593             return false;
40594         }
40595         if(this.maxValue && time > this.maxValue.getTime()){
40596             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40597             return false;
40598         }
40599         /*if(this.disabledDays){
40600             var day = value.getDay();
40601             for(var i = 0; i < this.disabledDays.length; i++) {
40602                 if(day === this.disabledDays[i]){
40603                     this.markInvalid(this.disabledDaysText);
40604                     return false;
40605                 }
40606             }
40607         }
40608         */
40609         var fvalue = this.formatDate(value);
40610         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40611             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40612             return false;
40613         }
40614         */
40615         return true;
40616     },
40617
40618     // private
40619     // Provides logic to override the default TriggerField.validateBlur which just returns true
40620     validateBlur : function(){
40621         return !this.menu || !this.menu.isVisible();
40622     },
40623
40624     /**
40625      * Returns the current date value of the date field.
40626      * @return {Date} The date value
40627      */
40628     getValue : function(){
40629         
40630         
40631         
40632         return  this.hiddenField ?
40633                 this.hiddenField.value :
40634                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40635     },
40636
40637     /**
40638      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40639      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40640      * (the default format used is "m/d/y").
40641      * <br />Usage:
40642      * <pre><code>
40643 //All of these calls set the same date value (May 4, 2006)
40644
40645 //Pass a date object:
40646 var dt = new Date('5/4/06');
40647 monthField.setValue(dt);
40648
40649 //Pass a date string (default format):
40650 monthField.setValue('5/4/06');
40651
40652 //Pass a date string (custom format):
40653 monthField.format = 'Y-m-d';
40654 monthField.setValue('2006-5-4');
40655 </code></pre>
40656      * @param {String/Date} date The date or valid date string
40657      */
40658     setValue : function(date){
40659         Roo.log('month setValue' + date);
40660         // can only be first of month..
40661         
40662         var val = this.parseDate(date);
40663         
40664         if (this.hiddenField) {
40665             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40666         }
40667         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40668         this.value = this.parseDate(date);
40669     },
40670
40671     // private
40672     parseDate : function(value){
40673         if(!value || value instanceof Date){
40674             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40675             return value;
40676         }
40677         var v = Date.parseDate(value, this.format);
40678         if (!v && this.useIso) {
40679             v = Date.parseDate(value, 'Y-m-d');
40680         }
40681         if (v) {
40682             // 
40683             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40684         }
40685         
40686         
40687         if(!v && this.altFormats){
40688             if(!this.altFormatsArray){
40689                 this.altFormatsArray = this.altFormats.split("|");
40690             }
40691             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40692                 v = Date.parseDate(value, this.altFormatsArray[i]);
40693             }
40694         }
40695         return v;
40696     },
40697
40698     // private
40699     formatDate : function(date, fmt){
40700         return (!date || !(date instanceof Date)) ?
40701                date : date.dateFormat(fmt || this.format);
40702     },
40703
40704     // private
40705     menuListeners : {
40706         select: function(m, d){
40707             this.setValue(d);
40708             this.fireEvent('select', this, d);
40709         },
40710         show : function(){ // retain focus styling
40711             this.onFocus();
40712         },
40713         hide : function(){
40714             this.focus.defer(10, this);
40715             var ml = this.menuListeners;
40716             this.menu.un("select", ml.select,  this);
40717             this.menu.un("show", ml.show,  this);
40718             this.menu.un("hide", ml.hide,  this);
40719         }
40720     },
40721     // private
40722     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40723     onTriggerClick : function(){
40724         if(this.disabled){
40725             return;
40726         }
40727         if(this.menu == null){
40728             this.menu = new Roo.menu.DateMenu();
40729            
40730         }
40731         
40732         Roo.apply(this.menu.picker,  {
40733             
40734             showClear: this.allowBlank,
40735             minDate : this.minValue,
40736             maxDate : this.maxValue,
40737             disabledDatesRE : this.ddMatch,
40738             disabledDatesText : this.disabledDatesText,
40739             
40740             format : this.useIso ? 'Y-m-d' : this.format,
40741             minText : String.format(this.minText, this.formatDate(this.minValue)),
40742             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40743             
40744         });
40745          this.menu.on(Roo.apply({}, this.menuListeners, {
40746             scope:this
40747         }));
40748        
40749         
40750         var m = this.menu;
40751         var p = m.picker;
40752         
40753         // hide month picker get's called when we called by 'before hide';
40754         
40755         var ignorehide = true;
40756         p.hideMonthPicker  = function(disableAnim){
40757             if (ignorehide) {
40758                 return;
40759             }
40760              if(this.monthPicker){
40761                 Roo.log("hideMonthPicker called");
40762                 if(disableAnim === true){
40763                     this.monthPicker.hide();
40764                 }else{
40765                     this.monthPicker.slideOut('t', {duration:.2});
40766                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40767                     p.fireEvent("select", this, this.value);
40768                     m.hide();
40769                 }
40770             }
40771         }
40772         
40773         Roo.log('picker set value');
40774         Roo.log(this.getValue());
40775         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40776         m.show(this.el, 'tl-bl?');
40777         ignorehide  = false;
40778         // this will trigger hideMonthPicker..
40779         
40780         
40781         // hidden the day picker
40782         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40783         
40784         
40785         
40786       
40787         
40788         p.showMonthPicker.defer(100, p);
40789     
40790         
40791        
40792     },
40793
40794     beforeBlur : function(){
40795         var v = this.parseDate(this.getRawValue());
40796         if(v){
40797             this.setValue(v);
40798         }
40799     }
40800
40801     /** @cfg {Boolean} grow @hide */
40802     /** @cfg {Number} growMin @hide */
40803     /** @cfg {Number} growMax @hide */
40804     /**
40805      * @hide
40806      * @method autoSize
40807      */
40808 });/*
40809  * Based on:
40810  * Ext JS Library 1.1.1
40811  * Copyright(c) 2006-2007, Ext JS, LLC.
40812  *
40813  * Originally Released Under LGPL - original licence link has changed is not relivant.
40814  *
40815  * Fork - LGPL
40816  * <script type="text/javascript">
40817  */
40818  
40819
40820 /**
40821  * @class Roo.form.ComboBox
40822  * @extends Roo.form.TriggerField
40823  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40824  * @constructor
40825  * Create a new ComboBox.
40826  * @param {Object} config Configuration options
40827  */
40828 Roo.form.ComboBox = function(config){
40829     Roo.form.ComboBox.superclass.constructor.call(this, config);
40830     this.addEvents({
40831         /**
40832          * @event expand
40833          * Fires when the dropdown list is expanded
40834              * @param {Roo.form.ComboBox} combo This combo box
40835              */
40836         'expand' : true,
40837         /**
40838          * @event collapse
40839          * Fires when the dropdown list is collapsed
40840              * @param {Roo.form.ComboBox} combo This combo box
40841              */
40842         'collapse' : true,
40843         /**
40844          * @event beforeselect
40845          * Fires before a list item is selected. Return false to cancel the selection.
40846              * @param {Roo.form.ComboBox} combo This combo box
40847              * @param {Roo.data.Record} record The data record returned from the underlying store
40848              * @param {Number} index The index of the selected item in the dropdown list
40849              */
40850         'beforeselect' : true,
40851         /**
40852          * @event select
40853          * Fires when a list item is selected
40854              * @param {Roo.form.ComboBox} combo This combo box
40855              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40856              * @param {Number} index The index of the selected item in the dropdown list
40857              */
40858         'select' : true,
40859         /**
40860          * @event beforequery
40861          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40862          * The event object passed has these properties:
40863              * @param {Roo.form.ComboBox} combo This combo box
40864              * @param {String} query The query
40865              * @param {Boolean} forceAll true to force "all" query
40866              * @param {Boolean} cancel true to cancel the query
40867              * @param {Object} e The query event object
40868              */
40869         'beforequery': true,
40870          /**
40871          * @event add
40872          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40873              * @param {Roo.form.ComboBox} combo This combo box
40874              */
40875         'add' : true,
40876         /**
40877          * @event edit
40878          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40879              * @param {Roo.form.ComboBox} combo This combo box
40880              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40881              */
40882         'edit' : true
40883         
40884         
40885     });
40886     if(this.transform){
40887         this.allowDomMove = false;
40888         var s = Roo.getDom(this.transform);
40889         if(!this.hiddenName){
40890             this.hiddenName = s.name;
40891         }
40892         if(!this.store){
40893             this.mode = 'local';
40894             var d = [], opts = s.options;
40895             for(var i = 0, len = opts.length;i < len; i++){
40896                 var o = opts[i];
40897                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40898                 if(o.selected) {
40899                     this.value = value;
40900                 }
40901                 d.push([value, o.text]);
40902             }
40903             this.store = new Roo.data.SimpleStore({
40904                 'id': 0,
40905                 fields: ['value', 'text'],
40906                 data : d
40907             });
40908             this.valueField = 'value';
40909             this.displayField = 'text';
40910         }
40911         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40912         if(!this.lazyRender){
40913             this.target = true;
40914             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40915             s.parentNode.removeChild(s); // remove it
40916             this.render(this.el.parentNode);
40917         }else{
40918             s.parentNode.removeChild(s); // remove it
40919         }
40920
40921     }
40922     if (this.store) {
40923         this.store = Roo.factory(this.store, Roo.data);
40924     }
40925     
40926     this.selectedIndex = -1;
40927     if(this.mode == 'local'){
40928         if(config.queryDelay === undefined){
40929             this.queryDelay = 10;
40930         }
40931         if(config.minChars === undefined){
40932             this.minChars = 0;
40933         }
40934     }
40935 };
40936
40937 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40938     /**
40939      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40940      */
40941     /**
40942      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40943      * rendering into an Roo.Editor, defaults to false)
40944      */
40945     /**
40946      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40947      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40948      */
40949     /**
40950      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40951      */
40952     /**
40953      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40954      * the dropdown list (defaults to undefined, with no header element)
40955      */
40956
40957      /**
40958      * @cfg {String/Roo.Template} tpl The template to use to render the output
40959      */
40960      
40961     // private
40962     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40963     /**
40964      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40965      */
40966     listWidth: undefined,
40967     /**
40968      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40969      * mode = 'remote' or 'text' if mode = 'local')
40970      */
40971     displayField: undefined,
40972     /**
40973      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40974      * mode = 'remote' or 'value' if mode = 'local'). 
40975      * Note: use of a valueField requires the user make a selection
40976      * in order for a value to be mapped.
40977      */
40978     valueField: undefined,
40979     
40980     
40981     /**
40982      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40983      * field's data value (defaults to the underlying DOM element's name)
40984      */
40985     hiddenName: undefined,
40986     /**
40987      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40988      */
40989     listClass: '',
40990     /**
40991      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40992      */
40993     selectedClass: 'x-combo-selected',
40994     /**
40995      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40996      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40997      * which displays a downward arrow icon).
40998      */
40999     triggerClass : 'x-form-arrow-trigger',
41000     /**
41001      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41002      */
41003     shadow:'sides',
41004     /**
41005      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41006      * anchor positions (defaults to 'tl-bl')
41007      */
41008     listAlign: 'tl-bl?',
41009     /**
41010      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41011      */
41012     maxHeight: 300,
41013     /**
41014      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41015      * query specified by the allQuery config option (defaults to 'query')
41016      */
41017     triggerAction: 'query',
41018     /**
41019      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41020      * (defaults to 4, does not apply if editable = false)
41021      */
41022     minChars : 4,
41023     /**
41024      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41025      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41026      */
41027     typeAhead: false,
41028     /**
41029      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41030      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41031      */
41032     queryDelay: 500,
41033     /**
41034      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41035      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41036      */
41037     pageSize: 0,
41038     /**
41039      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41040      * when editable = true (defaults to false)
41041      */
41042     selectOnFocus:false,
41043     /**
41044      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41045      */
41046     queryParam: 'query',
41047     /**
41048      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41049      * when mode = 'remote' (defaults to 'Loading...')
41050      */
41051     loadingText: 'Loading...',
41052     /**
41053      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41054      */
41055     resizable: false,
41056     /**
41057      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41058      */
41059     handleHeight : 8,
41060     /**
41061      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41062      * traditional select (defaults to true)
41063      */
41064     editable: true,
41065     /**
41066      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41067      */
41068     allQuery: '',
41069     /**
41070      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41071      */
41072     mode: 'remote',
41073     /**
41074      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41075      * listWidth has a higher value)
41076      */
41077     minListWidth : 70,
41078     /**
41079      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41080      * allow the user to set arbitrary text into the field (defaults to false)
41081      */
41082     forceSelection:false,
41083     /**
41084      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41085      * if typeAhead = true (defaults to 250)
41086      */
41087     typeAheadDelay : 250,
41088     /**
41089      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41090      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41091      */
41092     valueNotFoundText : undefined,
41093     /**
41094      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41095      */
41096     blockFocus : false,
41097     
41098     /**
41099      * @cfg {Boolean} disableClear Disable showing of clear button.
41100      */
41101     disableClear : false,
41102     /**
41103      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41104      */
41105     alwaysQuery : false,
41106     
41107     //private
41108     addicon : false,
41109     editicon: false,
41110     
41111     // element that contains real text value.. (when hidden is used..)
41112      
41113     // private
41114     onRender : function(ct, position){
41115         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41116         if(this.hiddenName){
41117             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41118                     'before', true);
41119             this.hiddenField.value =
41120                 this.hiddenValue !== undefined ? this.hiddenValue :
41121                 this.value !== undefined ? this.value : '';
41122
41123             // prevent input submission
41124             this.el.dom.removeAttribute('name');
41125              
41126              
41127         }
41128         if(Roo.isGecko){
41129             this.el.dom.setAttribute('autocomplete', 'off');
41130         }
41131
41132         var cls = 'x-combo-list';
41133
41134         this.list = new Roo.Layer({
41135             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41136         });
41137
41138         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41139         this.list.setWidth(lw);
41140         this.list.swallowEvent('mousewheel');
41141         this.assetHeight = 0;
41142
41143         if(this.title){
41144             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41145             this.assetHeight += this.header.getHeight();
41146         }
41147
41148         this.innerList = this.list.createChild({cls:cls+'-inner'});
41149         this.innerList.on('mouseover', this.onViewOver, this);
41150         this.innerList.on('mousemove', this.onViewMove, this);
41151         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41152         
41153         if(this.allowBlank && !this.pageSize && !this.disableClear){
41154             this.footer = this.list.createChild({cls:cls+'-ft'});
41155             this.pageTb = new Roo.Toolbar(this.footer);
41156            
41157         }
41158         if(this.pageSize){
41159             this.footer = this.list.createChild({cls:cls+'-ft'});
41160             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41161                     {pageSize: this.pageSize});
41162             
41163         }
41164         
41165         if (this.pageTb && this.allowBlank && !this.disableClear) {
41166             var _this = this;
41167             this.pageTb.add(new Roo.Toolbar.Fill(), {
41168                 cls: 'x-btn-icon x-btn-clear',
41169                 text: '&#160;',
41170                 handler: function()
41171                 {
41172                     _this.collapse();
41173                     _this.clearValue();
41174                     _this.onSelect(false, -1);
41175                 }
41176             });
41177         }
41178         if (this.footer) {
41179             this.assetHeight += this.footer.getHeight();
41180         }
41181         
41182
41183         if(!this.tpl){
41184             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41185         }
41186
41187         this.view = new Roo.View(this.innerList, this.tpl, {
41188             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41189         });
41190
41191         this.view.on('click', this.onViewClick, this);
41192
41193         this.store.on('beforeload', this.onBeforeLoad, this);
41194         this.store.on('load', this.onLoad, this);
41195         this.store.on('loadexception', this.onLoadException, this);
41196
41197         if(this.resizable){
41198             this.resizer = new Roo.Resizable(this.list,  {
41199                pinned:true, handles:'se'
41200             });
41201             this.resizer.on('resize', function(r, w, h){
41202                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41203                 this.listWidth = w;
41204                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41205                 this.restrictHeight();
41206             }, this);
41207             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41208         }
41209         if(!this.editable){
41210             this.editable = true;
41211             this.setEditable(false);
41212         }  
41213         
41214         
41215         if (typeof(this.events.add.listeners) != 'undefined') {
41216             
41217             this.addicon = this.wrap.createChild(
41218                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41219        
41220             this.addicon.on('click', function(e) {
41221                 this.fireEvent('add', this);
41222             }, this);
41223         }
41224         if (typeof(this.events.edit.listeners) != 'undefined') {
41225             
41226             this.editicon = this.wrap.createChild(
41227                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41228             if (this.addicon) {
41229                 this.editicon.setStyle('margin-left', '40px');
41230             }
41231             this.editicon.on('click', function(e) {
41232                 
41233                 // we fire even  if inothing is selected..
41234                 this.fireEvent('edit', this, this.lastData );
41235                 
41236             }, this);
41237         }
41238         
41239         
41240         
41241     },
41242
41243     // private
41244     initEvents : function(){
41245         Roo.form.ComboBox.superclass.initEvents.call(this);
41246
41247         this.keyNav = new Roo.KeyNav(this.el, {
41248             "up" : function(e){
41249                 this.inKeyMode = true;
41250                 this.selectPrev();
41251             },
41252
41253             "down" : function(e){
41254                 if(!this.isExpanded()){
41255                     this.onTriggerClick();
41256                 }else{
41257                     this.inKeyMode = true;
41258                     this.selectNext();
41259                 }
41260             },
41261
41262             "enter" : function(e){
41263                 this.onViewClick();
41264                 //return true;
41265             },
41266
41267             "esc" : function(e){
41268                 this.collapse();
41269             },
41270
41271             "tab" : function(e){
41272                 this.onViewClick(false);
41273                 this.fireEvent("specialkey", this, e);
41274                 return true;
41275             },
41276
41277             scope : this,
41278
41279             doRelay : function(foo, bar, hname){
41280                 if(hname == 'down' || this.scope.isExpanded()){
41281                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41282                 }
41283                 return true;
41284             },
41285
41286             forceKeyDown: true
41287         });
41288         this.queryDelay = Math.max(this.queryDelay || 10,
41289                 this.mode == 'local' ? 10 : 250);
41290         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41291         if(this.typeAhead){
41292             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41293         }
41294         if(this.editable !== false){
41295             this.el.on("keyup", this.onKeyUp, this);
41296         }
41297         if(this.forceSelection){
41298             this.on('blur', this.doForce, this);
41299         }
41300     },
41301
41302     onDestroy : function(){
41303         if(this.view){
41304             this.view.setStore(null);
41305             this.view.el.removeAllListeners();
41306             this.view.el.remove();
41307             this.view.purgeListeners();
41308         }
41309         if(this.list){
41310             this.list.destroy();
41311         }
41312         if(this.store){
41313             this.store.un('beforeload', this.onBeforeLoad, this);
41314             this.store.un('load', this.onLoad, this);
41315             this.store.un('loadexception', this.onLoadException, this);
41316         }
41317         Roo.form.ComboBox.superclass.onDestroy.call(this);
41318     },
41319
41320     // private
41321     fireKey : function(e){
41322         if(e.isNavKeyPress() && !this.list.isVisible()){
41323             this.fireEvent("specialkey", this, e);
41324         }
41325     },
41326
41327     // private
41328     onResize: function(w, h){
41329         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41330         
41331         if(typeof w != 'number'){
41332             // we do not handle it!?!?
41333             return;
41334         }
41335         var tw = this.trigger.getWidth();
41336         tw += this.addicon ? this.addicon.getWidth() : 0;
41337         tw += this.editicon ? this.editicon.getWidth() : 0;
41338         var x = w - tw;
41339         this.el.setWidth( this.adjustWidth('input', x));
41340             
41341         this.trigger.setStyle('left', x+'px');
41342         
41343         if(this.list && this.listWidth === undefined){
41344             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41345             this.list.setWidth(lw);
41346             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41347         }
41348         
41349     
41350         
41351     },
41352
41353     /**
41354      * Allow or prevent the user from directly editing the field text.  If false is passed,
41355      * the user will only be able to select from the items defined in the dropdown list.  This method
41356      * is the runtime equivalent of setting the 'editable' config option at config time.
41357      * @param {Boolean} value True to allow the user to directly edit the field text
41358      */
41359     setEditable : function(value){
41360         if(value == this.editable){
41361             return;
41362         }
41363         this.editable = value;
41364         if(!value){
41365             this.el.dom.setAttribute('readOnly', true);
41366             this.el.on('mousedown', this.onTriggerClick,  this);
41367             this.el.addClass('x-combo-noedit');
41368         }else{
41369             this.el.dom.setAttribute('readOnly', false);
41370             this.el.un('mousedown', this.onTriggerClick,  this);
41371             this.el.removeClass('x-combo-noedit');
41372         }
41373     },
41374
41375     // private
41376     onBeforeLoad : function(){
41377         if(!this.hasFocus){
41378             return;
41379         }
41380         this.innerList.update(this.loadingText ?
41381                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41382         this.restrictHeight();
41383         this.selectedIndex = -1;
41384     },
41385
41386     // private
41387     onLoad : function(){
41388         if(!this.hasFocus){
41389             return;
41390         }
41391         if(this.store.getCount() > 0){
41392             this.expand();
41393             this.restrictHeight();
41394             if(this.lastQuery == this.allQuery){
41395                 if(this.editable){
41396                     this.el.dom.select();
41397                 }
41398                 if(!this.selectByValue(this.value, true)){
41399                     this.select(0, true);
41400                 }
41401             }else{
41402                 this.selectNext();
41403                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41404                     this.taTask.delay(this.typeAheadDelay);
41405                 }
41406             }
41407         }else{
41408             this.onEmptyResults();
41409         }
41410         //this.el.focus();
41411     },
41412     // private
41413     onLoadException : function()
41414     {
41415         this.collapse();
41416         Roo.log(this.store.reader.jsonData);
41417         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41418             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41419         }
41420         
41421         
41422     },
41423     // private
41424     onTypeAhead : function(){
41425         if(this.store.getCount() > 0){
41426             var r = this.store.getAt(0);
41427             var newValue = r.data[this.displayField];
41428             var len = newValue.length;
41429             var selStart = this.getRawValue().length;
41430             if(selStart != len){
41431                 this.setRawValue(newValue);
41432                 this.selectText(selStart, newValue.length);
41433             }
41434         }
41435     },
41436
41437     // private
41438     onSelect : function(record, index){
41439         if(this.fireEvent('beforeselect', this, record, index) !== false){
41440             this.setFromData(index > -1 ? record.data : false);
41441             this.collapse();
41442             this.fireEvent('select', this, record, index);
41443         }
41444     },
41445
41446     /**
41447      * Returns the currently selected field value or empty string if no value is set.
41448      * @return {String} value The selected value
41449      */
41450     getValue : function(){
41451         if(this.valueField){
41452             return typeof this.value != 'undefined' ? this.value : '';
41453         }
41454         return Roo.form.ComboBox.superclass.getValue.call(this);
41455     },
41456
41457     /**
41458      * Clears any text/value currently set in the field
41459      */
41460     clearValue : function(){
41461         if(this.hiddenField){
41462             this.hiddenField.value = '';
41463         }
41464         this.value = '';
41465         this.setRawValue('');
41466         this.lastSelectionText = '';
41467         
41468     },
41469
41470     /**
41471      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41472      * will be displayed in the field.  If the value does not match the data value of an existing item,
41473      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41474      * Otherwise the field will be blank (although the value will still be set).
41475      * @param {String} value The value to match
41476      */
41477     setValue : function(v){
41478         var text = v;
41479         if(this.valueField){
41480             var r = this.findRecord(this.valueField, v);
41481             if(r){
41482                 text = r.data[this.displayField];
41483             }else if(this.valueNotFoundText !== undefined){
41484                 text = this.valueNotFoundText;
41485             }
41486         }
41487         this.lastSelectionText = text;
41488         if(this.hiddenField){
41489             this.hiddenField.value = v;
41490         }
41491         Roo.form.ComboBox.superclass.setValue.call(this, text);
41492         this.value = v;
41493     },
41494     /**
41495      * @property {Object} the last set data for the element
41496      */
41497     
41498     lastData : false,
41499     /**
41500      * Sets the value of the field based on a object which is related to the record format for the store.
41501      * @param {Object} value the value to set as. or false on reset?
41502      */
41503     setFromData : function(o){
41504         var dv = ''; // display value
41505         var vv = ''; // value value..
41506         this.lastData = o;
41507         if (this.displayField) {
41508             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41509         } else {
41510             // this is an error condition!!!
41511             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41512         }
41513         
41514         if(this.valueField){
41515             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41516         }
41517         if(this.hiddenField){
41518             this.hiddenField.value = vv;
41519             
41520             this.lastSelectionText = dv;
41521             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41522             this.value = vv;
41523             return;
41524         }
41525         // no hidden field.. - we store the value in 'value', but still display
41526         // display field!!!!
41527         this.lastSelectionText = dv;
41528         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41529         this.value = vv;
41530         
41531         
41532     },
41533     // private
41534     reset : function(){
41535         // overridden so that last data is reset..
41536         this.setValue(this.resetValue);
41537         this.clearInvalid();
41538         this.lastData = false;
41539         if (this.view) {
41540             this.view.clearSelections();
41541         }
41542     },
41543     // private
41544     findRecord : function(prop, value){
41545         var record;
41546         if(this.store.getCount() > 0){
41547             this.store.each(function(r){
41548                 if(r.data[prop] == value){
41549                     record = r;
41550                     return false;
41551                 }
41552                 return true;
41553             });
41554         }
41555         return record;
41556     },
41557     
41558     getName: function()
41559     {
41560         // returns hidden if it's set..
41561         if (!this.rendered) {return ''};
41562         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41563         
41564     },
41565     // private
41566     onViewMove : function(e, t){
41567         this.inKeyMode = false;
41568     },
41569
41570     // private
41571     onViewOver : function(e, t){
41572         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41573             return;
41574         }
41575         var item = this.view.findItemFromChild(t);
41576         if(item){
41577             var index = this.view.indexOf(item);
41578             this.select(index, false);
41579         }
41580     },
41581
41582     // private
41583     onViewClick : function(doFocus)
41584     {
41585         var index = this.view.getSelectedIndexes()[0];
41586         var r = this.store.getAt(index);
41587         if(r){
41588             this.onSelect(r, index);
41589         }
41590         if(doFocus !== false && !this.blockFocus){
41591             this.el.focus();
41592         }
41593     },
41594
41595     // private
41596     restrictHeight : function(){
41597         this.innerList.dom.style.height = '';
41598         var inner = this.innerList.dom;
41599         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41600         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41601         this.list.beginUpdate();
41602         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41603         this.list.alignTo(this.el, this.listAlign);
41604         this.list.endUpdate();
41605     },
41606
41607     // private
41608     onEmptyResults : function(){
41609         this.collapse();
41610     },
41611
41612     /**
41613      * Returns true if the dropdown list is expanded, else false.
41614      */
41615     isExpanded : function(){
41616         return this.list.isVisible();
41617     },
41618
41619     /**
41620      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41621      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41622      * @param {String} value The data value of the item to select
41623      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41624      * selected item if it is not currently in view (defaults to true)
41625      * @return {Boolean} True if the value matched an item in the list, else false
41626      */
41627     selectByValue : function(v, scrollIntoView){
41628         if(v !== undefined && v !== null){
41629             var r = this.findRecord(this.valueField || this.displayField, v);
41630             if(r){
41631                 this.select(this.store.indexOf(r), scrollIntoView);
41632                 return true;
41633             }
41634         }
41635         return false;
41636     },
41637
41638     /**
41639      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41640      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41641      * @param {Number} index The zero-based index of the list item to select
41642      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41643      * selected item if it is not currently in view (defaults to true)
41644      */
41645     select : function(index, scrollIntoView){
41646         this.selectedIndex = index;
41647         this.view.select(index);
41648         if(scrollIntoView !== false){
41649             var el = this.view.getNode(index);
41650             if(el){
41651                 this.innerList.scrollChildIntoView(el, false);
41652             }
41653         }
41654     },
41655
41656     // private
41657     selectNext : function(){
41658         var ct = this.store.getCount();
41659         if(ct > 0){
41660             if(this.selectedIndex == -1){
41661                 this.select(0);
41662             }else if(this.selectedIndex < ct-1){
41663                 this.select(this.selectedIndex+1);
41664             }
41665         }
41666     },
41667
41668     // private
41669     selectPrev : function(){
41670         var ct = this.store.getCount();
41671         if(ct > 0){
41672             if(this.selectedIndex == -1){
41673                 this.select(0);
41674             }else if(this.selectedIndex != 0){
41675                 this.select(this.selectedIndex-1);
41676             }
41677         }
41678     },
41679
41680     // private
41681     onKeyUp : function(e){
41682         if(this.editable !== false && !e.isSpecialKey()){
41683             this.lastKey = e.getKey();
41684             this.dqTask.delay(this.queryDelay);
41685         }
41686     },
41687
41688     // private
41689     validateBlur : function(){
41690         return !this.list || !this.list.isVisible();   
41691     },
41692
41693     // private
41694     initQuery : function(){
41695         this.doQuery(this.getRawValue());
41696     },
41697
41698     // private
41699     doForce : function(){
41700         if(this.el.dom.value.length > 0){
41701             this.el.dom.value =
41702                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41703              
41704         }
41705     },
41706
41707     /**
41708      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41709      * query allowing the query action to be canceled if needed.
41710      * @param {String} query The SQL query to execute
41711      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41712      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41713      * saved in the current store (defaults to false)
41714      */
41715     doQuery : function(q, forceAll){
41716         if(q === undefined || q === null){
41717             q = '';
41718         }
41719         var qe = {
41720             query: q,
41721             forceAll: forceAll,
41722             combo: this,
41723             cancel:false
41724         };
41725         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41726             return false;
41727         }
41728         q = qe.query;
41729         forceAll = qe.forceAll;
41730         if(forceAll === true || (q.length >= this.minChars)){
41731             if(this.lastQuery != q || this.alwaysQuery){
41732                 this.lastQuery = q;
41733                 if(this.mode == 'local'){
41734                     this.selectedIndex = -1;
41735                     if(forceAll){
41736                         this.store.clearFilter();
41737                     }else{
41738                         this.store.filter(this.displayField, q);
41739                     }
41740                     this.onLoad();
41741                 }else{
41742                     this.store.baseParams[this.queryParam] = q;
41743                     this.store.load({
41744                         params: this.getParams(q)
41745                     });
41746                     this.expand();
41747                 }
41748             }else{
41749                 this.selectedIndex = -1;
41750                 this.onLoad();   
41751             }
41752         }
41753     },
41754
41755     // private
41756     getParams : function(q){
41757         var p = {};
41758         //p[this.queryParam] = q;
41759         if(this.pageSize){
41760             p.start = 0;
41761             p.limit = this.pageSize;
41762         }
41763         return p;
41764     },
41765
41766     /**
41767      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41768      */
41769     collapse : function(){
41770         if(!this.isExpanded()){
41771             return;
41772         }
41773         this.list.hide();
41774         Roo.get(document).un('mousedown', this.collapseIf, this);
41775         Roo.get(document).un('mousewheel', this.collapseIf, this);
41776         if (!this.editable) {
41777             Roo.get(document).un('keydown', this.listKeyPress, this);
41778         }
41779         this.fireEvent('collapse', this);
41780     },
41781
41782     // private
41783     collapseIf : function(e){
41784         if(!e.within(this.wrap) && !e.within(this.list)){
41785             this.collapse();
41786         }
41787     },
41788
41789     /**
41790      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41791      */
41792     expand : function(){
41793         if(this.isExpanded() || !this.hasFocus){
41794             return;
41795         }
41796         this.list.alignTo(this.el, this.listAlign);
41797         this.list.show();
41798         Roo.get(document).on('mousedown', this.collapseIf, this);
41799         Roo.get(document).on('mousewheel', this.collapseIf, this);
41800         if (!this.editable) {
41801             Roo.get(document).on('keydown', this.listKeyPress, this);
41802         }
41803         
41804         this.fireEvent('expand', this);
41805     },
41806
41807     // private
41808     // Implements the default empty TriggerField.onTriggerClick function
41809     onTriggerClick : function(){
41810         if(this.disabled){
41811             return;
41812         }
41813         if(this.isExpanded()){
41814             this.collapse();
41815             if (!this.blockFocus) {
41816                 this.el.focus();
41817             }
41818             
41819         }else {
41820             this.hasFocus = true;
41821             if(this.triggerAction == 'all') {
41822                 this.doQuery(this.allQuery, true);
41823             } else {
41824                 this.doQuery(this.getRawValue());
41825             }
41826             if (!this.blockFocus) {
41827                 this.el.focus();
41828             }
41829         }
41830     },
41831     listKeyPress : function(e)
41832     {
41833         //Roo.log('listkeypress');
41834         // scroll to first matching element based on key pres..
41835         if (e.isSpecialKey()) {
41836             return false;
41837         }
41838         var k = String.fromCharCode(e.getKey()).toUpperCase();
41839         //Roo.log(k);
41840         var match  = false;
41841         var csel = this.view.getSelectedNodes();
41842         var cselitem = false;
41843         if (csel.length) {
41844             var ix = this.view.indexOf(csel[0]);
41845             cselitem  = this.store.getAt(ix);
41846             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41847                 cselitem = false;
41848             }
41849             
41850         }
41851         
41852         this.store.each(function(v) { 
41853             if (cselitem) {
41854                 // start at existing selection.
41855                 if (cselitem.id == v.id) {
41856                     cselitem = false;
41857                 }
41858                 return;
41859             }
41860                 
41861             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41862                 match = this.store.indexOf(v);
41863                 return false;
41864             }
41865         }, this);
41866         
41867         if (match === false) {
41868             return true; // no more action?
41869         }
41870         // scroll to?
41871         this.view.select(match);
41872         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41873         sn.scrollIntoView(sn.dom.parentNode, false);
41874     }
41875
41876     /** 
41877     * @cfg {Boolean} grow 
41878     * @hide 
41879     */
41880     /** 
41881     * @cfg {Number} growMin 
41882     * @hide 
41883     */
41884     /** 
41885     * @cfg {Number} growMax 
41886     * @hide 
41887     */
41888     /**
41889      * @hide
41890      * @method autoSize
41891      */
41892 });/*
41893  * Copyright(c) 2010-2012, Roo J Solutions Limited
41894  *
41895  * Licence LGPL
41896  *
41897  */
41898
41899 /**
41900  * @class Roo.form.ComboBoxArray
41901  * @extends Roo.form.TextField
41902  * A facebook style adder... for lists of email / people / countries  etc...
41903  * pick multiple items from a combo box, and shows each one.
41904  *
41905  *  Fred [x]  Brian [x]  [Pick another |v]
41906  *
41907  *
41908  *  For this to work: it needs various extra information
41909  *    - normal combo problay has
41910  *      name, hiddenName
41911  *    + displayField, valueField
41912  *
41913  *    For our purpose...
41914  *
41915  *
41916  *   If we change from 'extends' to wrapping...
41917  *   
41918  *  
41919  *
41920  
41921  
41922  * @constructor
41923  * Create a new ComboBoxArray.
41924  * @param {Object} config Configuration options
41925  */
41926  
41927
41928 Roo.form.ComboBoxArray = function(config)
41929 {
41930     this.addEvents({
41931         /**
41932          * @event beforeremove
41933          * Fires before remove the value from the list
41934              * @param {Roo.form.ComboBoxArray} _self This combo box array
41935              * @param {Roo.form.ComboBoxArray.Item} item removed item
41936              */
41937         'beforeremove' : true,
41938         /**
41939          * @event remove
41940          * Fires when remove the value from the list
41941              * @param {Roo.form.ComboBoxArray} _self This combo box array
41942              * @param {Roo.form.ComboBoxArray.Item} item removed item
41943              */
41944         'remove' : true
41945         
41946         
41947     });
41948     
41949     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41950     
41951     this.items = new Roo.util.MixedCollection(false);
41952     
41953     // construct the child combo...
41954     
41955     
41956     
41957     
41958    
41959     
41960 }
41961
41962  
41963 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41964
41965     /**
41966      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41967      */
41968     
41969     lastData : false,
41970     
41971     // behavies liek a hiddne field
41972     inputType:      'hidden',
41973     /**
41974      * @cfg {Number} width The width of the box that displays the selected element
41975      */ 
41976     width:          300,
41977
41978     
41979     
41980     /**
41981      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41982      */
41983     name : false,
41984     /**
41985      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41986      */
41987     hiddenName : false,
41988     
41989     
41990     // private the array of items that are displayed..
41991     items  : false,
41992     // private - the hidden field el.
41993     hiddenEl : false,
41994     // private - the filed el..
41995     el : false,
41996     
41997     //validateValue : function() { return true; }, // all values are ok!
41998     //onAddClick: function() { },
41999     
42000     onRender : function(ct, position) 
42001     {
42002         
42003         // create the standard hidden element
42004         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42005         
42006         
42007         // give fake names to child combo;
42008         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42009         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42010         
42011         this.combo = Roo.factory(this.combo, Roo.form);
42012         this.combo.onRender(ct, position);
42013         if (typeof(this.combo.width) != 'undefined') {
42014             this.combo.onResize(this.combo.width,0);
42015         }
42016         
42017         this.combo.initEvents();
42018         
42019         // assigned so form know we need to do this..
42020         this.store          = this.combo.store;
42021         this.valueField     = this.combo.valueField;
42022         this.displayField   = this.combo.displayField ;
42023         
42024         
42025         this.combo.wrap.addClass('x-cbarray-grp');
42026         
42027         var cbwrap = this.combo.wrap.createChild(
42028             {tag: 'div', cls: 'x-cbarray-cb'},
42029             this.combo.el.dom
42030         );
42031         
42032              
42033         this.hiddenEl = this.combo.wrap.createChild({
42034             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42035         });
42036         this.el = this.combo.wrap.createChild({
42037             tag: 'input',  type:'hidden' , name: this.name, value : ''
42038         });
42039          //   this.el.dom.removeAttribute("name");
42040         
42041         
42042         this.outerWrap = this.combo.wrap;
42043         this.wrap = cbwrap;
42044         
42045         this.outerWrap.setWidth(this.width);
42046         this.outerWrap.dom.removeChild(this.el.dom);
42047         
42048         this.wrap.dom.appendChild(this.el.dom);
42049         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42050         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42051         
42052         this.combo.trigger.setStyle('position','relative');
42053         this.combo.trigger.setStyle('left', '0px');
42054         this.combo.trigger.setStyle('top', '2px');
42055         
42056         this.combo.el.setStyle('vertical-align', 'text-bottom');
42057         
42058         //this.trigger.setStyle('vertical-align', 'top');
42059         
42060         // this should use the code from combo really... on('add' ....)
42061         if (this.adder) {
42062             
42063         
42064             this.adder = this.outerWrap.createChild(
42065                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42066             var _t = this;
42067             this.adder.on('click', function(e) {
42068                 _t.fireEvent('adderclick', this, e);
42069             }, _t);
42070         }
42071         //var _t = this;
42072         //this.adder.on('click', this.onAddClick, _t);
42073         
42074         
42075         this.combo.on('select', function(cb, rec, ix) {
42076             this.addItem(rec.data);
42077             
42078             cb.setValue('');
42079             cb.el.dom.value = '';
42080             //cb.lastData = rec.data;
42081             // add to list
42082             
42083         }, this);
42084         
42085         
42086     },
42087     
42088     
42089     getName: function()
42090     {
42091         // returns hidden if it's set..
42092         if (!this.rendered) {return ''};
42093         return  this.hiddenName ? this.hiddenName : this.name;
42094         
42095     },
42096     
42097     
42098     onResize: function(w, h){
42099         
42100         return;
42101         // not sure if this is needed..
42102         //this.combo.onResize(w,h);
42103         
42104         if(typeof w != 'number'){
42105             // we do not handle it!?!?
42106             return;
42107         }
42108         var tw = this.combo.trigger.getWidth();
42109         tw += this.addicon ? this.addicon.getWidth() : 0;
42110         tw += this.editicon ? this.editicon.getWidth() : 0;
42111         var x = w - tw;
42112         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42113             
42114         this.combo.trigger.setStyle('left', '0px');
42115         
42116         if(this.list && this.listWidth === undefined){
42117             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42118             this.list.setWidth(lw);
42119             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42120         }
42121         
42122     
42123         
42124     },
42125     
42126     addItem: function(rec)
42127     {
42128         var valueField = this.combo.valueField;
42129         var displayField = this.combo.displayField;
42130         if (this.items.indexOfKey(rec[valueField]) > -1) {
42131             //console.log("GOT " + rec.data.id);
42132             return;
42133         }
42134         
42135         var x = new Roo.form.ComboBoxArray.Item({
42136             //id : rec[this.idField],
42137             data : rec,
42138             displayField : displayField ,
42139             tipField : displayField ,
42140             cb : this
42141         });
42142         // use the 
42143         this.items.add(rec[valueField],x);
42144         // add it before the element..
42145         this.updateHiddenEl();
42146         x.render(this.outerWrap, this.wrap.dom);
42147         // add the image handler..
42148     },
42149     
42150     updateHiddenEl : function()
42151     {
42152         this.validate();
42153         if (!this.hiddenEl) {
42154             return;
42155         }
42156         var ar = [];
42157         var idField = this.combo.valueField;
42158         
42159         this.items.each(function(f) {
42160             ar.push(f.data[idField]);
42161            
42162         });
42163         this.hiddenEl.dom.value = ar.join(',');
42164         this.validate();
42165     },
42166     
42167     reset : function()
42168     {
42169         this.items.clear();
42170         
42171         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42172            el.remove();
42173         });
42174         
42175         this.el.dom.value = '';
42176         if (this.hiddenEl) {
42177             this.hiddenEl.dom.value = '';
42178         }
42179         
42180     },
42181     getValue: function()
42182     {
42183         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42184     },
42185     setValue: function(v) // not a valid action - must use addItems..
42186     {
42187          
42188         this.reset();
42189         
42190         
42191         
42192         if (this.store.isLocal && (typeof(v) == 'string')) {
42193             // then we can use the store to find the values..
42194             // comma seperated at present.. this needs to allow JSON based encoding..
42195             this.hiddenEl.value  = v;
42196             var v_ar = [];
42197             Roo.each(v.split(','), function(k) {
42198                 Roo.log("CHECK " + this.valueField + ',' + k);
42199                 var li = this.store.query(this.valueField, k);
42200                 if (!li.length) {
42201                     return;
42202                 }
42203                 var add = {};
42204                 add[this.valueField] = k;
42205                 add[this.displayField] = li.item(0).data[this.displayField];
42206                 
42207                 this.addItem(add);
42208             }, this) 
42209              
42210         }
42211         if (typeof(v) == 'object' ) {
42212             // then let's assume it's an array of objects..
42213             Roo.each(v, function(l) {
42214                 this.addItem(l);
42215             }, this);
42216              
42217         }
42218         
42219         
42220     },
42221     setFromData: function(v)
42222     {
42223         // this recieves an object, if setValues is called.
42224         this.reset();
42225         this.el.dom.value = v[this.displayField];
42226         this.hiddenEl.dom.value = v[this.valueField];
42227         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42228             return;
42229         }
42230         var kv = v[this.valueField];
42231         var dv = v[this.displayField];
42232         kv = typeof(kv) != 'string' ? '' : kv;
42233         dv = typeof(dv) != 'string' ? '' : dv;
42234         
42235         
42236         var keys = kv.split(',');
42237         var display = dv.split(',');
42238         for (var i = 0 ; i < keys.length; i++) {
42239             
42240             add = {};
42241             add[this.valueField] = keys[i];
42242             add[this.displayField] = display[i];
42243             this.addItem(add);
42244         }
42245       
42246         
42247     },
42248     
42249     /**
42250      * Validates the combox array value
42251      * @return {Boolean} True if the value is valid, else false
42252      */
42253     validate : function(){
42254         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42255             this.clearInvalid();
42256             return true;
42257         }
42258         return false;
42259     },
42260     
42261     validateValue : function(value){
42262         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42263         
42264     },
42265     
42266     /*@
42267      * overide
42268      * 
42269      */
42270     isDirty : function() {
42271         if(this.disabled) {
42272             return false;
42273         }
42274         
42275         try {
42276             var d = Roo.decode(String(this.originalValue));
42277         } catch (e) {
42278             return String(this.getValue()) !== String(this.originalValue);
42279         }
42280         
42281         var originalValue = [];
42282         
42283         for (var i = 0; i < d.length; i++){
42284             originalValue.push(d[i][this.valueField]);
42285         }
42286         
42287         return String(this.getValue()) !== String(originalValue.join(','));
42288         
42289     }
42290     
42291 });
42292
42293
42294
42295 /**
42296  * @class Roo.form.ComboBoxArray.Item
42297  * @extends Roo.BoxComponent
42298  * A selected item in the list
42299  *  Fred [x]  Brian [x]  [Pick another |v]
42300  * 
42301  * @constructor
42302  * Create a new item.
42303  * @param {Object} config Configuration options
42304  */
42305  
42306 Roo.form.ComboBoxArray.Item = function(config) {
42307     config.id = Roo.id();
42308     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42309 }
42310
42311 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42312     data : {},
42313     cb: false,
42314     displayField : false,
42315     tipField : false,
42316     
42317     
42318     defaultAutoCreate : {
42319         tag: 'div',
42320         cls: 'x-cbarray-item',
42321         cn : [ 
42322             { tag: 'div' },
42323             {
42324                 tag: 'img',
42325                 width:16,
42326                 height : 16,
42327                 src : Roo.BLANK_IMAGE_URL ,
42328                 align: 'center'
42329             }
42330         ]
42331         
42332     },
42333     
42334  
42335     onRender : function(ct, position)
42336     {
42337         Roo.form.Field.superclass.onRender.call(this, ct, position);
42338         
42339         if(!this.el){
42340             var cfg = this.getAutoCreate();
42341             this.el = ct.createChild(cfg, position);
42342         }
42343         
42344         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42345         
42346         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42347             this.cb.renderer(this.data) :
42348             String.format('{0}',this.data[this.displayField]);
42349         
42350             
42351         this.el.child('div').dom.setAttribute('qtip',
42352                         String.format('{0}',this.data[this.tipField])
42353         );
42354         
42355         this.el.child('img').on('click', this.remove, this);
42356         
42357     },
42358    
42359     remove : function()
42360     {
42361         if(this.cb.disabled){
42362             return;
42363         }
42364         
42365         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42366             this.cb.items.remove(this);
42367             this.el.child('img').un('click', this.remove, this);
42368             this.el.remove();
42369             this.cb.updateHiddenEl();
42370
42371             this.cb.fireEvent('remove', this.cb, this);
42372         }
42373         
42374     }
42375 });/*
42376  * Based on:
42377  * Ext JS Library 1.1.1
42378  * Copyright(c) 2006-2007, Ext JS, LLC.
42379  *
42380  * Originally Released Under LGPL - original licence link has changed is not relivant.
42381  *
42382  * Fork - LGPL
42383  * <script type="text/javascript">
42384  */
42385 /**
42386  * @class Roo.form.Checkbox
42387  * @extends Roo.form.Field
42388  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42389  * @constructor
42390  * Creates a new Checkbox
42391  * @param {Object} config Configuration options
42392  */
42393 Roo.form.Checkbox = function(config){
42394     Roo.form.Checkbox.superclass.constructor.call(this, config);
42395     this.addEvents({
42396         /**
42397          * @event check
42398          * Fires when the checkbox is checked or unchecked.
42399              * @param {Roo.form.Checkbox} this This checkbox
42400              * @param {Boolean} checked The new checked value
42401              */
42402         check : true
42403     });
42404 };
42405
42406 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42407     /**
42408      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42409      */
42410     focusClass : undefined,
42411     /**
42412      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42413      */
42414     fieldClass: "x-form-field",
42415     /**
42416      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42417      */
42418     checked: false,
42419     /**
42420      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42421      * {tag: "input", type: "checkbox", autocomplete: "off"})
42422      */
42423     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42424     /**
42425      * @cfg {String} boxLabel The text that appears beside the checkbox
42426      */
42427     boxLabel : "",
42428     /**
42429      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42430      */  
42431     inputValue : '1',
42432     /**
42433      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42434      */
42435      valueOff: '0', // value when not checked..
42436
42437     actionMode : 'viewEl', 
42438     //
42439     // private
42440     itemCls : 'x-menu-check-item x-form-item',
42441     groupClass : 'x-menu-group-item',
42442     inputType : 'hidden',
42443     
42444     
42445     inSetChecked: false, // check that we are not calling self...
42446     
42447     inputElement: false, // real input element?
42448     basedOn: false, // ????
42449     
42450     isFormField: true, // not sure where this is needed!!!!
42451
42452     onResize : function(){
42453         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42454         if(!this.boxLabel){
42455             this.el.alignTo(this.wrap, 'c-c');
42456         }
42457     },
42458
42459     initEvents : function(){
42460         Roo.form.Checkbox.superclass.initEvents.call(this);
42461         this.el.on("click", this.onClick,  this);
42462         this.el.on("change", this.onClick,  this);
42463     },
42464
42465
42466     getResizeEl : function(){
42467         return this.wrap;
42468     },
42469
42470     getPositionEl : function(){
42471         return this.wrap;
42472     },
42473
42474     // private
42475     onRender : function(ct, position){
42476         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42477         /*
42478         if(this.inputValue !== undefined){
42479             this.el.dom.value = this.inputValue;
42480         }
42481         */
42482         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42483         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42484         var viewEl = this.wrap.createChild({ 
42485             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42486         this.viewEl = viewEl;   
42487         this.wrap.on('click', this.onClick,  this); 
42488         
42489         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42490         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42491         
42492         
42493         
42494         if(this.boxLabel){
42495             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42496         //    viewEl.on('click', this.onClick,  this); 
42497         }
42498         //if(this.checked){
42499             this.setChecked(this.checked);
42500         //}else{
42501             //this.checked = this.el.dom;
42502         //}
42503
42504     },
42505
42506     // private
42507     initValue : Roo.emptyFn,
42508
42509     /**
42510      * Returns the checked state of the checkbox.
42511      * @return {Boolean} True if checked, else false
42512      */
42513     getValue : function(){
42514         if(this.el){
42515             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42516         }
42517         return this.valueOff;
42518         
42519     },
42520
42521         // private
42522     onClick : function(){ 
42523         if (this.disabled) {
42524             return;
42525         }
42526         this.setChecked(!this.checked);
42527
42528         //if(this.el.dom.checked != this.checked){
42529         //    this.setValue(this.el.dom.checked);
42530        // }
42531     },
42532
42533     /**
42534      * Sets the checked state of the checkbox.
42535      * On is always based on a string comparison between inputValue and the param.
42536      * @param {Boolean/String} value - the value to set 
42537      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42538      */
42539     setValue : function(v,suppressEvent){
42540         
42541         
42542         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42543         //if(this.el && this.el.dom){
42544         //    this.el.dom.checked = this.checked;
42545         //    this.el.dom.defaultChecked = this.checked;
42546         //}
42547         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42548         //this.fireEvent("check", this, this.checked);
42549     },
42550     // private..
42551     setChecked : function(state,suppressEvent)
42552     {
42553         if (this.inSetChecked) {
42554             this.checked = state;
42555             return;
42556         }
42557         
42558     
42559         if(this.wrap){
42560             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42561         }
42562         this.checked = state;
42563         if(suppressEvent !== true){
42564             this.fireEvent('check', this, state);
42565         }
42566         this.inSetChecked = true;
42567         this.el.dom.value = state ? this.inputValue : this.valueOff;
42568         this.inSetChecked = false;
42569         
42570     },
42571     // handle setting of hidden value by some other method!!?!?
42572     setFromHidden: function()
42573     {
42574         if(!this.el){
42575             return;
42576         }
42577         //console.log("SET FROM HIDDEN");
42578         //alert('setFrom hidden');
42579         this.setValue(this.el.dom.value);
42580     },
42581     
42582     onDestroy : function()
42583     {
42584         if(this.viewEl){
42585             Roo.get(this.viewEl).remove();
42586         }
42587          
42588         Roo.form.Checkbox.superclass.onDestroy.call(this);
42589     },
42590     
42591     setBoxLabel : function(str)
42592     {
42593         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42594     }
42595
42596 });/*
42597  * Based on:
42598  * Ext JS Library 1.1.1
42599  * Copyright(c) 2006-2007, Ext JS, LLC.
42600  *
42601  * Originally Released Under LGPL - original licence link has changed is not relivant.
42602  *
42603  * Fork - LGPL
42604  * <script type="text/javascript">
42605  */
42606  
42607 /**
42608  * @class Roo.form.Radio
42609  * @extends Roo.form.Checkbox
42610  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42611  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42612  * @constructor
42613  * Creates a new Radio
42614  * @param {Object} config Configuration options
42615  */
42616 Roo.form.Radio = function(){
42617     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42618 };
42619 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42620     inputType: 'radio',
42621
42622     /**
42623      * If this radio is part of a group, it will return the selected value
42624      * @return {String}
42625      */
42626     getGroupValue : function(){
42627         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42628     },
42629     
42630     
42631     onRender : function(ct, position){
42632         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42633         
42634         if(this.inputValue !== undefined){
42635             this.el.dom.value = this.inputValue;
42636         }
42637          
42638         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42639         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42640         //var viewEl = this.wrap.createChild({ 
42641         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42642         //this.viewEl = viewEl;   
42643         //this.wrap.on('click', this.onClick,  this); 
42644         
42645         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42646         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42647         
42648         
42649         
42650         if(this.boxLabel){
42651             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42652         //    viewEl.on('click', this.onClick,  this); 
42653         }
42654          if(this.checked){
42655             this.el.dom.checked =   'checked' ;
42656         }
42657          
42658     } 
42659     
42660     
42661 });//<script type="text/javascript">
42662
42663 /*
42664  * Based  Ext JS Library 1.1.1
42665  * Copyright(c) 2006-2007, Ext JS, LLC.
42666  * LGPL
42667  *
42668  */
42669  
42670 /**
42671  * @class Roo.HtmlEditorCore
42672  * @extends Roo.Component
42673  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42674  *
42675  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42676  */
42677
42678 Roo.HtmlEditorCore = function(config){
42679     
42680     
42681     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42682     
42683     
42684     this.addEvents({
42685         /**
42686          * @event initialize
42687          * Fires when the editor is fully initialized (including the iframe)
42688          * @param {Roo.HtmlEditorCore} this
42689          */
42690         initialize: true,
42691         /**
42692          * @event activate
42693          * Fires when the editor is first receives the focus. Any insertion must wait
42694          * until after this event.
42695          * @param {Roo.HtmlEditorCore} this
42696          */
42697         activate: true,
42698          /**
42699          * @event beforesync
42700          * Fires before the textarea is updated with content from the editor iframe. Return false
42701          * to cancel the sync.
42702          * @param {Roo.HtmlEditorCore} this
42703          * @param {String} html
42704          */
42705         beforesync: true,
42706          /**
42707          * @event beforepush
42708          * Fires before the iframe editor is updated with content from the textarea. Return false
42709          * to cancel the push.
42710          * @param {Roo.HtmlEditorCore} this
42711          * @param {String} html
42712          */
42713         beforepush: true,
42714          /**
42715          * @event sync
42716          * Fires when the textarea is updated with content from the editor iframe.
42717          * @param {Roo.HtmlEditorCore} this
42718          * @param {String} html
42719          */
42720         sync: true,
42721          /**
42722          * @event push
42723          * Fires when the iframe editor is updated with content from the textarea.
42724          * @param {Roo.HtmlEditorCore} this
42725          * @param {String} html
42726          */
42727         push: true,
42728         
42729         /**
42730          * @event editorevent
42731          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42732          * @param {Roo.HtmlEditorCore} this
42733          */
42734         editorevent: true
42735         
42736     });
42737     
42738     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42739     
42740     // defaults : white / black...
42741     this.applyBlacklists();
42742     
42743     
42744     
42745 };
42746
42747
42748 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42749
42750
42751      /**
42752      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42753      */
42754     
42755     owner : false,
42756     
42757      /**
42758      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42759      *                        Roo.resizable.
42760      */
42761     resizable : false,
42762      /**
42763      * @cfg {Number} height (in pixels)
42764      */   
42765     height: 300,
42766    /**
42767      * @cfg {Number} width (in pixels)
42768      */   
42769     width: 500,
42770     
42771     /**
42772      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42773      * 
42774      */
42775     stylesheets: false,
42776     
42777     // id of frame..
42778     frameId: false,
42779     
42780     // private properties
42781     validationEvent : false,
42782     deferHeight: true,
42783     initialized : false,
42784     activated : false,
42785     sourceEditMode : false,
42786     onFocus : Roo.emptyFn,
42787     iframePad:3,
42788     hideMode:'offsets',
42789     
42790     clearUp: true,
42791     
42792     // blacklist + whitelisted elements..
42793     black: false,
42794     white: false,
42795      
42796     
42797
42798     /**
42799      * Protected method that will not generally be called directly. It
42800      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42801      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42802      */
42803     getDocMarkup : function(){
42804         // body styles..
42805         var st = '';
42806         
42807         // inherit styels from page...?? 
42808         if (this.stylesheets === false) {
42809             
42810             Roo.get(document.head).select('style').each(function(node) {
42811                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42812             });
42813             
42814             Roo.get(document.head).select('link').each(function(node) { 
42815                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42816             });
42817             
42818         } else if (!this.stylesheets.length) {
42819                 // simple..
42820                 st = '<style type="text/css">' +
42821                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42822                    '</style>';
42823         } else { 
42824             
42825         }
42826         
42827         st +=  '<style type="text/css">' +
42828             'IMG { cursor: pointer } ' +
42829         '</style>';
42830
42831         
42832         return '<html><head>' + st  +
42833             //<style type="text/css">' +
42834             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42835             //'</style>' +
42836             ' </head><body class="roo-htmleditor-body"></body></html>';
42837     },
42838
42839     // private
42840     onRender : function(ct, position)
42841     {
42842         var _t = this;
42843         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42844         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42845         
42846         
42847         this.el.dom.style.border = '0 none';
42848         this.el.dom.setAttribute('tabIndex', -1);
42849         this.el.addClass('x-hidden hide');
42850         
42851         
42852         
42853         if(Roo.isIE){ // fix IE 1px bogus margin
42854             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42855         }
42856        
42857         
42858         this.frameId = Roo.id();
42859         
42860          
42861         
42862         var iframe = this.owner.wrap.createChild({
42863             tag: 'iframe',
42864             cls: 'form-control', // bootstrap..
42865             id: this.frameId,
42866             name: this.frameId,
42867             frameBorder : 'no',
42868             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42869         }, this.el
42870         );
42871         
42872         
42873         this.iframe = iframe.dom;
42874
42875          this.assignDocWin();
42876         
42877         this.doc.designMode = 'on';
42878        
42879         this.doc.open();
42880         this.doc.write(this.getDocMarkup());
42881         this.doc.close();
42882
42883         
42884         var task = { // must defer to wait for browser to be ready
42885             run : function(){
42886                 //console.log("run task?" + this.doc.readyState);
42887                 this.assignDocWin();
42888                 if(this.doc.body || this.doc.readyState == 'complete'){
42889                     try {
42890                         this.doc.designMode="on";
42891                     } catch (e) {
42892                         return;
42893                     }
42894                     Roo.TaskMgr.stop(task);
42895                     this.initEditor.defer(10, this);
42896                 }
42897             },
42898             interval : 10,
42899             duration: 10000,
42900             scope: this
42901         };
42902         Roo.TaskMgr.start(task);
42903
42904     },
42905
42906     // private
42907     onResize : function(w, h)
42908     {
42909          Roo.log('resize: ' +w + ',' + h );
42910         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42911         if(!this.iframe){
42912             return;
42913         }
42914         if(typeof w == 'number'){
42915             
42916             this.iframe.style.width = w + 'px';
42917         }
42918         if(typeof h == 'number'){
42919             
42920             this.iframe.style.height = h + 'px';
42921             if(this.doc){
42922                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42923             }
42924         }
42925         
42926     },
42927
42928     /**
42929      * Toggles the editor between standard and source edit mode.
42930      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42931      */
42932     toggleSourceEdit : function(sourceEditMode){
42933         
42934         this.sourceEditMode = sourceEditMode === true;
42935         
42936         if(this.sourceEditMode){
42937  
42938             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42939             
42940         }else{
42941             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42942             //this.iframe.className = '';
42943             this.deferFocus();
42944         }
42945         //this.setSize(this.owner.wrap.getSize());
42946         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42947     },
42948
42949     
42950   
42951
42952     /**
42953      * Protected method that will not generally be called directly. If you need/want
42954      * custom HTML cleanup, this is the method you should override.
42955      * @param {String} html The HTML to be cleaned
42956      * return {String} The cleaned HTML
42957      */
42958     cleanHtml : function(html){
42959         html = String(html);
42960         if(html.length > 5){
42961             if(Roo.isSafari){ // strip safari nonsense
42962                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42963             }
42964         }
42965         if(html == '&nbsp;'){
42966             html = '';
42967         }
42968         return html;
42969     },
42970
42971     /**
42972      * HTML Editor -> Textarea
42973      * Protected method that will not generally be called directly. Syncs the contents
42974      * of the editor iframe with the textarea.
42975      */
42976     syncValue : function(){
42977         if(this.initialized){
42978             var bd = (this.doc.body || this.doc.documentElement);
42979             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42980             var html = bd.innerHTML;
42981             if(Roo.isSafari){
42982                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42983                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42984                 if(m && m[1]){
42985                     html = '<div style="'+m[0]+'">' + html + '</div>';
42986                 }
42987             }
42988             html = this.cleanHtml(html);
42989             // fix up the special chars.. normaly like back quotes in word...
42990             // however we do not want to do this with chinese..
42991             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42992                 var cc = b.charCodeAt();
42993                 if (
42994                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42995                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42996                     (cc >= 0xf900 && cc < 0xfb00 )
42997                 ) {
42998                         return b;
42999                 }
43000                 return "&#"+cc+";" 
43001             });
43002             if(this.owner.fireEvent('beforesync', this, html) !== false){
43003                 this.el.dom.value = html;
43004                 this.owner.fireEvent('sync', this, html);
43005             }
43006         }
43007     },
43008
43009     /**
43010      * Protected method that will not generally be called directly. Pushes the value of the textarea
43011      * into the iframe editor.
43012      */
43013     pushValue : function(){
43014         if(this.initialized){
43015             var v = this.el.dom.value.trim();
43016             
43017 //            if(v.length < 1){
43018 //                v = '&#160;';
43019 //            }
43020             
43021             if(this.owner.fireEvent('beforepush', this, v) !== false){
43022                 var d = (this.doc.body || this.doc.documentElement);
43023                 d.innerHTML = v;
43024                 this.cleanUpPaste();
43025                 this.el.dom.value = d.innerHTML;
43026                 this.owner.fireEvent('push', this, v);
43027             }
43028         }
43029     },
43030
43031     // private
43032     deferFocus : function(){
43033         this.focus.defer(10, this);
43034     },
43035
43036     // doc'ed in Field
43037     focus : function(){
43038         if(this.win && !this.sourceEditMode){
43039             this.win.focus();
43040         }else{
43041             this.el.focus();
43042         }
43043     },
43044     
43045     assignDocWin: function()
43046     {
43047         var iframe = this.iframe;
43048         
43049          if(Roo.isIE){
43050             this.doc = iframe.contentWindow.document;
43051             this.win = iframe.contentWindow;
43052         } else {
43053 //            if (!Roo.get(this.frameId)) {
43054 //                return;
43055 //            }
43056 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43057 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43058             
43059             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43060                 return;
43061             }
43062             
43063             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43064             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43065         }
43066     },
43067     
43068     // private
43069     initEditor : function(){
43070         //console.log("INIT EDITOR");
43071         this.assignDocWin();
43072         
43073         
43074         
43075         this.doc.designMode="on";
43076         this.doc.open();
43077         this.doc.write(this.getDocMarkup());
43078         this.doc.close();
43079         
43080         var dbody = (this.doc.body || this.doc.documentElement);
43081         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43082         // this copies styles from the containing element into thsi one..
43083         // not sure why we need all of this..
43084         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43085         
43086         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43087         //ss['background-attachment'] = 'fixed'; // w3c
43088         dbody.bgProperties = 'fixed'; // ie
43089         //Roo.DomHelper.applyStyles(dbody, ss);
43090         Roo.EventManager.on(this.doc, {
43091             //'mousedown': this.onEditorEvent,
43092             'mouseup': this.onEditorEvent,
43093             'dblclick': this.onEditorEvent,
43094             'click': this.onEditorEvent,
43095             'keyup': this.onEditorEvent,
43096             buffer:100,
43097             scope: this
43098         });
43099         if(Roo.isGecko){
43100             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43101         }
43102         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43103             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43104         }
43105         this.initialized = true;
43106
43107         this.owner.fireEvent('initialize', this);
43108         this.pushValue();
43109     },
43110
43111     // private
43112     onDestroy : function(){
43113         
43114         
43115         
43116         if(this.rendered){
43117             
43118             //for (var i =0; i < this.toolbars.length;i++) {
43119             //    // fixme - ask toolbars for heights?
43120             //    this.toolbars[i].onDestroy();
43121            // }
43122             
43123             //this.wrap.dom.innerHTML = '';
43124             //this.wrap.remove();
43125         }
43126     },
43127
43128     // private
43129     onFirstFocus : function(){
43130         
43131         this.assignDocWin();
43132         
43133         
43134         this.activated = true;
43135          
43136     
43137         if(Roo.isGecko){ // prevent silly gecko errors
43138             this.win.focus();
43139             var s = this.win.getSelection();
43140             if(!s.focusNode || s.focusNode.nodeType != 3){
43141                 var r = s.getRangeAt(0);
43142                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43143                 r.collapse(true);
43144                 this.deferFocus();
43145             }
43146             try{
43147                 this.execCmd('useCSS', true);
43148                 this.execCmd('styleWithCSS', false);
43149             }catch(e){}
43150         }
43151         this.owner.fireEvent('activate', this);
43152     },
43153
43154     // private
43155     adjustFont: function(btn){
43156         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43157         //if(Roo.isSafari){ // safari
43158         //    adjust *= 2;
43159        // }
43160         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43161         if(Roo.isSafari){ // safari
43162             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43163             v =  (v < 10) ? 10 : v;
43164             v =  (v > 48) ? 48 : v;
43165             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43166             
43167         }
43168         
43169         
43170         v = Math.max(1, v+adjust);
43171         
43172         this.execCmd('FontSize', v  );
43173     },
43174
43175     onEditorEvent : function(e)
43176     {
43177         this.owner.fireEvent('editorevent', this, e);
43178       //  this.updateToolbar();
43179         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43180     },
43181
43182     insertTag : function(tg)
43183     {
43184         // could be a bit smarter... -> wrap the current selected tRoo..
43185         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43186             
43187             range = this.createRange(this.getSelection());
43188             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43189             wrappingNode.appendChild(range.extractContents());
43190             range.insertNode(wrappingNode);
43191
43192             return;
43193             
43194             
43195             
43196         }
43197         this.execCmd("formatblock",   tg);
43198         
43199     },
43200     
43201     insertText : function(txt)
43202     {
43203         
43204         
43205         var range = this.createRange();
43206         range.deleteContents();
43207                //alert(Sender.getAttribute('label'));
43208                
43209         range.insertNode(this.doc.createTextNode(txt));
43210     } ,
43211     
43212      
43213
43214     /**
43215      * Executes a Midas editor command on the editor document and performs necessary focus and
43216      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43217      * @param {String} cmd The Midas command
43218      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43219      */
43220     relayCmd : function(cmd, value){
43221         this.win.focus();
43222         this.execCmd(cmd, value);
43223         this.owner.fireEvent('editorevent', this);
43224         //this.updateToolbar();
43225         this.owner.deferFocus();
43226     },
43227
43228     /**
43229      * Executes a Midas editor command directly on the editor document.
43230      * For visual commands, you should use {@link #relayCmd} instead.
43231      * <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     execCmd : function(cmd, value){
43236         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43237         this.syncValue();
43238     },
43239  
43240  
43241    
43242     /**
43243      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43244      * to insert tRoo.
43245      * @param {String} text | dom node.. 
43246      */
43247     insertAtCursor : function(text)
43248     {
43249         
43250         
43251         
43252         if(!this.activated){
43253             return;
43254         }
43255         /*
43256         if(Roo.isIE){
43257             this.win.focus();
43258             var r = this.doc.selection.createRange();
43259             if(r){
43260                 r.collapse(true);
43261                 r.pasteHTML(text);
43262                 this.syncValue();
43263                 this.deferFocus();
43264             
43265             }
43266             return;
43267         }
43268         */
43269         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43270             this.win.focus();
43271             
43272             
43273             // from jquery ui (MIT licenced)
43274             var range, node;
43275             var win = this.win;
43276             
43277             if (win.getSelection && win.getSelection().getRangeAt) {
43278                 range = win.getSelection().getRangeAt(0);
43279                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43280                 range.insertNode(node);
43281             } else if (win.document.selection && win.document.selection.createRange) {
43282                 // no firefox support
43283                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43284                 win.document.selection.createRange().pasteHTML(txt);
43285             } else {
43286                 // no firefox support
43287                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43288                 this.execCmd('InsertHTML', txt);
43289             } 
43290             
43291             this.syncValue();
43292             
43293             this.deferFocus();
43294         }
43295     },
43296  // private
43297     mozKeyPress : function(e){
43298         if(e.ctrlKey){
43299             var c = e.getCharCode(), cmd;
43300           
43301             if(c > 0){
43302                 c = String.fromCharCode(c).toLowerCase();
43303                 switch(c){
43304                     case 'b':
43305                         cmd = 'bold';
43306                         break;
43307                     case 'i':
43308                         cmd = 'italic';
43309                         break;
43310                     
43311                     case 'u':
43312                         cmd = 'underline';
43313                         break;
43314                     
43315                     case 'v':
43316                         this.cleanUpPaste.defer(100, this);
43317                         return;
43318                         
43319                 }
43320                 if(cmd){
43321                     this.win.focus();
43322                     this.execCmd(cmd);
43323                     this.deferFocus();
43324                     e.preventDefault();
43325                 }
43326                 
43327             }
43328         }
43329     },
43330
43331     // private
43332     fixKeys : function(){ // load time branching for fastest keydown performance
43333         if(Roo.isIE){
43334             return function(e){
43335                 var k = e.getKey(), r;
43336                 if(k == e.TAB){
43337                     e.stopEvent();
43338                     r = this.doc.selection.createRange();
43339                     if(r){
43340                         r.collapse(true);
43341                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43342                         this.deferFocus();
43343                     }
43344                     return;
43345                 }
43346                 
43347                 if(k == e.ENTER){
43348                     r = this.doc.selection.createRange();
43349                     if(r){
43350                         var target = r.parentElement();
43351                         if(!target || target.tagName.toLowerCase() != 'li'){
43352                             e.stopEvent();
43353                             r.pasteHTML('<br />');
43354                             r.collapse(false);
43355                             r.select();
43356                         }
43357                     }
43358                 }
43359                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43360                     this.cleanUpPaste.defer(100, this);
43361                     return;
43362                 }
43363                 
43364                 
43365             };
43366         }else if(Roo.isOpera){
43367             return function(e){
43368                 var k = e.getKey();
43369                 if(k == e.TAB){
43370                     e.stopEvent();
43371                     this.win.focus();
43372                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43373                     this.deferFocus();
43374                 }
43375                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43376                     this.cleanUpPaste.defer(100, this);
43377                     return;
43378                 }
43379                 
43380             };
43381         }else if(Roo.isSafari){
43382             return function(e){
43383                 var k = e.getKey();
43384                 
43385                 if(k == e.TAB){
43386                     e.stopEvent();
43387                     this.execCmd('InsertText','\t');
43388                     this.deferFocus();
43389                     return;
43390                 }
43391                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43392                     this.cleanUpPaste.defer(100, this);
43393                     return;
43394                 }
43395                 
43396              };
43397         }
43398     }(),
43399     
43400     getAllAncestors: function()
43401     {
43402         var p = this.getSelectedNode();
43403         var a = [];
43404         if (!p) {
43405             a.push(p); // push blank onto stack..
43406             p = this.getParentElement();
43407         }
43408         
43409         
43410         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43411             a.push(p);
43412             p = p.parentNode;
43413         }
43414         a.push(this.doc.body);
43415         return a;
43416     },
43417     lastSel : false,
43418     lastSelNode : false,
43419     
43420     
43421     getSelection : function() 
43422     {
43423         this.assignDocWin();
43424         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43425     },
43426     
43427     getSelectedNode: function() 
43428     {
43429         // this may only work on Gecko!!!
43430         
43431         // should we cache this!!!!
43432         
43433         
43434         
43435          
43436         var range = this.createRange(this.getSelection()).cloneRange();
43437         
43438         if (Roo.isIE) {
43439             var parent = range.parentElement();
43440             while (true) {
43441                 var testRange = range.duplicate();
43442                 testRange.moveToElementText(parent);
43443                 if (testRange.inRange(range)) {
43444                     break;
43445                 }
43446                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43447                     break;
43448                 }
43449                 parent = parent.parentElement;
43450             }
43451             return parent;
43452         }
43453         
43454         // is ancestor a text element.
43455         var ac =  range.commonAncestorContainer;
43456         if (ac.nodeType == 3) {
43457             ac = ac.parentNode;
43458         }
43459         
43460         var ar = ac.childNodes;
43461          
43462         var nodes = [];
43463         var other_nodes = [];
43464         var has_other_nodes = false;
43465         for (var i=0;i<ar.length;i++) {
43466             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43467                 continue;
43468             }
43469             // fullly contained node.
43470             
43471             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43472                 nodes.push(ar[i]);
43473                 continue;
43474             }
43475             
43476             // probably selected..
43477             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43478                 other_nodes.push(ar[i]);
43479                 continue;
43480             }
43481             // outer..
43482             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43483                 continue;
43484             }
43485             
43486             
43487             has_other_nodes = true;
43488         }
43489         if (!nodes.length && other_nodes.length) {
43490             nodes= other_nodes;
43491         }
43492         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43493             return false;
43494         }
43495         
43496         return nodes[0];
43497     },
43498     createRange: function(sel)
43499     {
43500         // this has strange effects when using with 
43501         // top toolbar - not sure if it's a great idea.
43502         //this.editor.contentWindow.focus();
43503         if (typeof sel != "undefined") {
43504             try {
43505                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43506             } catch(e) {
43507                 return this.doc.createRange();
43508             }
43509         } else {
43510             return this.doc.createRange();
43511         }
43512     },
43513     getParentElement: function()
43514     {
43515         
43516         this.assignDocWin();
43517         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43518         
43519         var range = this.createRange(sel);
43520          
43521         try {
43522             var p = range.commonAncestorContainer;
43523             while (p.nodeType == 3) { // text node
43524                 p = p.parentNode;
43525             }
43526             return p;
43527         } catch (e) {
43528             return null;
43529         }
43530     
43531     },
43532     /***
43533      *
43534      * Range intersection.. the hard stuff...
43535      *  '-1' = before
43536      *  '0' = hits..
43537      *  '1' = after.
43538      *         [ -- selected range --- ]
43539      *   [fail]                        [fail]
43540      *
43541      *    basically..
43542      *      if end is before start or  hits it. fail.
43543      *      if start is after end or hits it fail.
43544      *
43545      *   if either hits (but other is outside. - then it's not 
43546      *   
43547      *    
43548      **/
43549     
43550     
43551     // @see http://www.thismuchiknow.co.uk/?p=64.
43552     rangeIntersectsNode : function(range, node)
43553     {
43554         var nodeRange = node.ownerDocument.createRange();
43555         try {
43556             nodeRange.selectNode(node);
43557         } catch (e) {
43558             nodeRange.selectNodeContents(node);
43559         }
43560     
43561         var rangeStartRange = range.cloneRange();
43562         rangeStartRange.collapse(true);
43563     
43564         var rangeEndRange = range.cloneRange();
43565         rangeEndRange.collapse(false);
43566     
43567         var nodeStartRange = nodeRange.cloneRange();
43568         nodeStartRange.collapse(true);
43569     
43570         var nodeEndRange = nodeRange.cloneRange();
43571         nodeEndRange.collapse(false);
43572     
43573         return rangeStartRange.compareBoundaryPoints(
43574                  Range.START_TO_START, nodeEndRange) == -1 &&
43575                rangeEndRange.compareBoundaryPoints(
43576                  Range.START_TO_START, nodeStartRange) == 1;
43577         
43578          
43579     },
43580     rangeCompareNode : function(range, node)
43581     {
43582         var nodeRange = node.ownerDocument.createRange();
43583         try {
43584             nodeRange.selectNode(node);
43585         } catch (e) {
43586             nodeRange.selectNodeContents(node);
43587         }
43588         
43589         
43590         range.collapse(true);
43591     
43592         nodeRange.collapse(true);
43593      
43594         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43595         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43596          
43597         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43598         
43599         var nodeIsBefore   =  ss == 1;
43600         var nodeIsAfter    = ee == -1;
43601         
43602         if (nodeIsBefore && nodeIsAfter) {
43603             return 0; // outer
43604         }
43605         if (!nodeIsBefore && nodeIsAfter) {
43606             return 1; //right trailed.
43607         }
43608         
43609         if (nodeIsBefore && !nodeIsAfter) {
43610             return 2;  // left trailed.
43611         }
43612         // fully contined.
43613         return 3;
43614     },
43615
43616     // private? - in a new class?
43617     cleanUpPaste :  function()
43618     {
43619         // cleans up the whole document..
43620         Roo.log('cleanuppaste');
43621         
43622         this.cleanUpChildren(this.doc.body);
43623         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43624         if (clean != this.doc.body.innerHTML) {
43625             this.doc.body.innerHTML = clean;
43626         }
43627         
43628     },
43629     
43630     cleanWordChars : function(input) {// change the chars to hex code
43631         var he = Roo.HtmlEditorCore;
43632         
43633         var output = input;
43634         Roo.each(he.swapCodes, function(sw) { 
43635             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43636             
43637             output = output.replace(swapper, sw[1]);
43638         });
43639         
43640         return output;
43641     },
43642     
43643     
43644     cleanUpChildren : function (n)
43645     {
43646         if (!n.childNodes.length) {
43647             return;
43648         }
43649         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43650            this.cleanUpChild(n.childNodes[i]);
43651         }
43652     },
43653     
43654     
43655         
43656     
43657     cleanUpChild : function (node)
43658     {
43659         var ed = this;
43660         //console.log(node);
43661         if (node.nodeName == "#text") {
43662             // clean up silly Windows -- stuff?
43663             return; 
43664         }
43665         if (node.nodeName == "#comment") {
43666             node.parentNode.removeChild(node);
43667             // clean up silly Windows -- stuff?
43668             return; 
43669         }
43670         var lcname = node.tagName.toLowerCase();
43671         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43672         // whitelist of tags..
43673         
43674         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43675             // remove node.
43676             node.parentNode.removeChild(node);
43677             return;
43678             
43679         }
43680         
43681         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43682         
43683         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43684         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43685         
43686         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43687         //    remove_keep_children = true;
43688         //}
43689         
43690         if (remove_keep_children) {
43691             this.cleanUpChildren(node);
43692             // inserts everything just before this node...
43693             while (node.childNodes.length) {
43694                 var cn = node.childNodes[0];
43695                 node.removeChild(cn);
43696                 node.parentNode.insertBefore(cn, node);
43697             }
43698             node.parentNode.removeChild(node);
43699             return;
43700         }
43701         
43702         if (!node.attributes || !node.attributes.length) {
43703             this.cleanUpChildren(node);
43704             return;
43705         }
43706         
43707         function cleanAttr(n,v)
43708         {
43709             
43710             if (v.match(/^\./) || v.match(/^\//)) {
43711                 return;
43712             }
43713             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43714                 return;
43715             }
43716             if (v.match(/^#/)) {
43717                 return;
43718             }
43719 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43720             node.removeAttribute(n);
43721             
43722         }
43723         
43724         var cwhite = this.cwhite;
43725         var cblack = this.cblack;
43726             
43727         function cleanStyle(n,v)
43728         {
43729             if (v.match(/expression/)) { //XSS?? should we even bother..
43730                 node.removeAttribute(n);
43731                 return;
43732             }
43733             
43734             var parts = v.split(/;/);
43735             var clean = [];
43736             
43737             Roo.each(parts, function(p) {
43738                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43739                 if (!p.length) {
43740                     return true;
43741                 }
43742                 var l = p.split(':').shift().replace(/\s+/g,'');
43743                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43744                 
43745                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43746 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43747                     //node.removeAttribute(n);
43748                     return true;
43749                 }
43750                 //Roo.log()
43751                 // only allow 'c whitelisted system attributes'
43752                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43753 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43754                     //node.removeAttribute(n);
43755                     return true;
43756                 }
43757                 
43758                 
43759                  
43760                 
43761                 clean.push(p);
43762                 return true;
43763             });
43764             if (clean.length) { 
43765                 node.setAttribute(n, clean.join(';'));
43766             } else {
43767                 node.removeAttribute(n);
43768             }
43769             
43770         }
43771         
43772         
43773         for (var i = node.attributes.length-1; i > -1 ; i--) {
43774             var a = node.attributes[i];
43775             //console.log(a);
43776             
43777             if (a.name.toLowerCase().substr(0,2)=='on')  {
43778                 node.removeAttribute(a.name);
43779                 continue;
43780             }
43781             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43782                 node.removeAttribute(a.name);
43783                 continue;
43784             }
43785             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43786                 cleanAttr(a.name,a.value); // fixme..
43787                 continue;
43788             }
43789             if (a.name == 'style') {
43790                 cleanStyle(a.name,a.value);
43791                 continue;
43792             }
43793             /// clean up MS crap..
43794             // tecnically this should be a list of valid class'es..
43795             
43796             
43797             if (a.name == 'class') {
43798                 if (a.value.match(/^Mso/)) {
43799                     node.className = '';
43800                 }
43801                 
43802                 if (a.value.match(/body/)) {
43803                     node.className = '';
43804                 }
43805                 continue;
43806             }
43807             
43808             // style cleanup!?
43809             // class cleanup?
43810             
43811         }
43812         
43813         
43814         this.cleanUpChildren(node);
43815         
43816         
43817     },
43818     
43819     /**
43820      * Clean up MS wordisms...
43821      */
43822     cleanWord : function(node)
43823     {
43824         
43825         
43826         if (!node) {
43827             this.cleanWord(this.doc.body);
43828             return;
43829         }
43830         if (node.nodeName == "#text") {
43831             // clean up silly Windows -- stuff?
43832             return; 
43833         }
43834         if (node.nodeName == "#comment") {
43835             node.parentNode.removeChild(node);
43836             // clean up silly Windows -- stuff?
43837             return; 
43838         }
43839         
43840         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43841             node.parentNode.removeChild(node);
43842             return;
43843         }
43844         
43845         // remove - but keep children..
43846         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43847             while (node.childNodes.length) {
43848                 var cn = node.childNodes[0];
43849                 node.removeChild(cn);
43850                 node.parentNode.insertBefore(cn, node);
43851             }
43852             node.parentNode.removeChild(node);
43853             this.iterateChildren(node, this.cleanWord);
43854             return;
43855         }
43856         // clean styles
43857         if (node.className.length) {
43858             
43859             var cn = node.className.split(/\W+/);
43860             var cna = [];
43861             Roo.each(cn, function(cls) {
43862                 if (cls.match(/Mso[a-zA-Z]+/)) {
43863                     return;
43864                 }
43865                 cna.push(cls);
43866             });
43867             node.className = cna.length ? cna.join(' ') : '';
43868             if (!cna.length) {
43869                 node.removeAttribute("class");
43870             }
43871         }
43872         
43873         if (node.hasAttribute("lang")) {
43874             node.removeAttribute("lang");
43875         }
43876         
43877         if (node.hasAttribute("style")) {
43878             
43879             var styles = node.getAttribute("style").split(";");
43880             var nstyle = [];
43881             Roo.each(styles, function(s) {
43882                 if (!s.match(/:/)) {
43883                     return;
43884                 }
43885                 var kv = s.split(":");
43886                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43887                     return;
43888                 }
43889                 // what ever is left... we allow.
43890                 nstyle.push(s);
43891             });
43892             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43893             if (!nstyle.length) {
43894                 node.removeAttribute('style');
43895             }
43896         }
43897         this.iterateChildren(node, this.cleanWord);
43898         
43899         
43900         
43901     },
43902     /**
43903      * iterateChildren of a Node, calling fn each time, using this as the scole..
43904      * @param {DomNode} node node to iterate children of.
43905      * @param {Function} fn method of this class to call on each item.
43906      */
43907     iterateChildren : function(node, fn)
43908     {
43909         if (!node.childNodes.length) {
43910                 return;
43911         }
43912         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43913            fn.call(this, node.childNodes[i])
43914         }
43915     },
43916     
43917     
43918     /**
43919      * cleanTableWidths.
43920      *
43921      * Quite often pasting from word etc.. results in tables with column and widths.
43922      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43923      *
43924      */
43925     cleanTableWidths : function(node)
43926     {
43927          
43928          
43929         if (!node) {
43930             this.cleanTableWidths(this.doc.body);
43931             return;
43932         }
43933         
43934         // ignore list...
43935         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43936             return; 
43937         }
43938         Roo.log(node.tagName);
43939         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43940             this.iterateChildren(node, this.cleanTableWidths);
43941             return;
43942         }
43943         if (node.hasAttribute('width')) {
43944             node.removeAttribute('width');
43945         }
43946         
43947          
43948         if (node.hasAttribute("style")) {
43949             // pretty basic...
43950             
43951             var styles = node.getAttribute("style").split(";");
43952             var nstyle = [];
43953             Roo.each(styles, function(s) {
43954                 if (!s.match(/:/)) {
43955                     return;
43956                 }
43957                 var kv = s.split(":");
43958                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43959                     return;
43960                 }
43961                 // what ever is left... we allow.
43962                 nstyle.push(s);
43963             });
43964             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43965             if (!nstyle.length) {
43966                 node.removeAttribute('style');
43967             }
43968         }
43969         
43970         this.iterateChildren(node, this.cleanTableWidths);
43971         
43972         
43973     },
43974     
43975     
43976     
43977     
43978     domToHTML : function(currentElement, depth, nopadtext) {
43979         
43980         depth = depth || 0;
43981         nopadtext = nopadtext || false;
43982     
43983         if (!currentElement) {
43984             return this.domToHTML(this.doc.body);
43985         }
43986         
43987         //Roo.log(currentElement);
43988         var j;
43989         var allText = false;
43990         var nodeName = currentElement.nodeName;
43991         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43992         
43993         if  (nodeName == '#text') {
43994             
43995             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43996         }
43997         
43998         
43999         var ret = '';
44000         if (nodeName != 'BODY') {
44001              
44002             var i = 0;
44003             // Prints the node tagName, such as <A>, <IMG>, etc
44004             if (tagName) {
44005                 var attr = [];
44006                 for(i = 0; i < currentElement.attributes.length;i++) {
44007                     // quoting?
44008                     var aname = currentElement.attributes.item(i).name;
44009                     if (!currentElement.attributes.item(i).value.length) {
44010                         continue;
44011                     }
44012                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44013                 }
44014                 
44015                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44016             } 
44017             else {
44018                 
44019                 // eack
44020             }
44021         } else {
44022             tagName = false;
44023         }
44024         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44025             return ret;
44026         }
44027         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44028             nopadtext = true;
44029         }
44030         
44031         
44032         // Traverse the tree
44033         i = 0;
44034         var currentElementChild = currentElement.childNodes.item(i);
44035         var allText = true;
44036         var innerHTML  = '';
44037         lastnode = '';
44038         while (currentElementChild) {
44039             // Formatting code (indent the tree so it looks nice on the screen)
44040             var nopad = nopadtext;
44041             if (lastnode == 'SPAN') {
44042                 nopad  = true;
44043             }
44044             // text
44045             if  (currentElementChild.nodeName == '#text') {
44046                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44047                 toadd = nopadtext ? toadd : toadd.trim();
44048                 if (!nopad && toadd.length > 80) {
44049                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44050                 }
44051                 innerHTML  += toadd;
44052                 
44053                 i++;
44054                 currentElementChild = currentElement.childNodes.item(i);
44055                 lastNode = '';
44056                 continue;
44057             }
44058             allText = false;
44059             
44060             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44061                 
44062             // Recursively traverse the tree structure of the child node
44063             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44064             lastnode = currentElementChild.nodeName;
44065             i++;
44066             currentElementChild=currentElement.childNodes.item(i);
44067         }
44068         
44069         ret += innerHTML;
44070         
44071         if (!allText) {
44072                 // The remaining code is mostly for formatting the tree
44073             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44074         }
44075         
44076         
44077         if (tagName) {
44078             ret+= "</"+tagName+">";
44079         }
44080         return ret;
44081         
44082     },
44083         
44084     applyBlacklists : function()
44085     {
44086         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44087         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44088         
44089         this.white = [];
44090         this.black = [];
44091         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44092             if (b.indexOf(tag) > -1) {
44093                 return;
44094             }
44095             this.white.push(tag);
44096             
44097         }, this);
44098         
44099         Roo.each(w, function(tag) {
44100             if (b.indexOf(tag) > -1) {
44101                 return;
44102             }
44103             if (this.white.indexOf(tag) > -1) {
44104                 return;
44105             }
44106             this.white.push(tag);
44107             
44108         }, this);
44109         
44110         
44111         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44112             if (w.indexOf(tag) > -1) {
44113                 return;
44114             }
44115             this.black.push(tag);
44116             
44117         }, this);
44118         
44119         Roo.each(b, function(tag) {
44120             if (w.indexOf(tag) > -1) {
44121                 return;
44122             }
44123             if (this.black.indexOf(tag) > -1) {
44124                 return;
44125             }
44126             this.black.push(tag);
44127             
44128         }, this);
44129         
44130         
44131         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44132         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44133         
44134         this.cwhite = [];
44135         this.cblack = [];
44136         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44137             if (b.indexOf(tag) > -1) {
44138                 return;
44139             }
44140             this.cwhite.push(tag);
44141             
44142         }, this);
44143         
44144         Roo.each(w, function(tag) {
44145             if (b.indexOf(tag) > -1) {
44146                 return;
44147             }
44148             if (this.cwhite.indexOf(tag) > -1) {
44149                 return;
44150             }
44151             this.cwhite.push(tag);
44152             
44153         }, this);
44154         
44155         
44156         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44157             if (w.indexOf(tag) > -1) {
44158                 return;
44159             }
44160             this.cblack.push(tag);
44161             
44162         }, this);
44163         
44164         Roo.each(b, function(tag) {
44165             if (w.indexOf(tag) > -1) {
44166                 return;
44167             }
44168             if (this.cblack.indexOf(tag) > -1) {
44169                 return;
44170             }
44171             this.cblack.push(tag);
44172             
44173         }, this);
44174     },
44175     
44176     setStylesheets : function(stylesheets)
44177     {
44178         if(typeof(stylesheets) == 'string'){
44179             Roo.get(this.iframe.contentDocument.head).createChild({
44180                 tag : 'link',
44181                 rel : 'stylesheet',
44182                 type : 'text/css',
44183                 href : stylesheets
44184             });
44185             
44186             return;
44187         }
44188         var _this = this;
44189      
44190         Roo.each(stylesheets, function(s) {
44191             if(!s.length){
44192                 return;
44193             }
44194             
44195             Roo.get(_this.iframe.contentDocument.head).createChild({
44196                 tag : 'link',
44197                 rel : 'stylesheet',
44198                 type : 'text/css',
44199                 href : s
44200             });
44201         });
44202
44203         
44204     },
44205     
44206     removeStylesheets : function()
44207     {
44208         var _this = this;
44209         
44210         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44211             s.remove();
44212         });
44213     }
44214     
44215     // hide stuff that is not compatible
44216     /**
44217      * @event blur
44218      * @hide
44219      */
44220     /**
44221      * @event change
44222      * @hide
44223      */
44224     /**
44225      * @event focus
44226      * @hide
44227      */
44228     /**
44229      * @event specialkey
44230      * @hide
44231      */
44232     /**
44233      * @cfg {String} fieldClass @hide
44234      */
44235     /**
44236      * @cfg {String} focusClass @hide
44237      */
44238     /**
44239      * @cfg {String} autoCreate @hide
44240      */
44241     /**
44242      * @cfg {String} inputType @hide
44243      */
44244     /**
44245      * @cfg {String} invalidClass @hide
44246      */
44247     /**
44248      * @cfg {String} invalidText @hide
44249      */
44250     /**
44251      * @cfg {String} msgFx @hide
44252      */
44253     /**
44254      * @cfg {String} validateOnBlur @hide
44255      */
44256 });
44257
44258 Roo.HtmlEditorCore.white = [
44259         'area', 'br', 'img', 'input', 'hr', 'wbr',
44260         
44261        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44262        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44263        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44264        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44265        'table',   'ul',         'xmp', 
44266        
44267        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44268       'thead',   'tr', 
44269      
44270       'dir', 'menu', 'ol', 'ul', 'dl',
44271        
44272       'embed',  'object'
44273 ];
44274
44275
44276 Roo.HtmlEditorCore.black = [
44277     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44278         'applet', // 
44279         'base',   'basefont', 'bgsound', 'blink',  'body', 
44280         'frame',  'frameset', 'head',    'html',   'ilayer', 
44281         'iframe', 'layer',  'link',     'meta',    'object',   
44282         'script', 'style' ,'title',  'xml' // clean later..
44283 ];
44284 Roo.HtmlEditorCore.clean = [
44285     'script', 'style', 'title', 'xml'
44286 ];
44287 Roo.HtmlEditorCore.remove = [
44288     'font'
44289 ];
44290 // attributes..
44291
44292 Roo.HtmlEditorCore.ablack = [
44293     'on'
44294 ];
44295     
44296 Roo.HtmlEditorCore.aclean = [ 
44297     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44298 ];
44299
44300 // protocols..
44301 Roo.HtmlEditorCore.pwhite= [
44302         'http',  'https',  'mailto'
44303 ];
44304
44305 // white listed style attributes.
44306 Roo.HtmlEditorCore.cwhite= [
44307       //  'text-align', /// default is to allow most things..
44308       
44309          
44310 //        'font-size'//??
44311 ];
44312
44313 // black listed style attributes.
44314 Roo.HtmlEditorCore.cblack= [
44315       //  'font-size' -- this can be set by the project 
44316 ];
44317
44318
44319 Roo.HtmlEditorCore.swapCodes   =[ 
44320     [    8211, "--" ], 
44321     [    8212, "--" ], 
44322     [    8216,  "'" ],  
44323     [    8217, "'" ],  
44324     [    8220, '"' ],  
44325     [    8221, '"' ],  
44326     [    8226, "*" ],  
44327     [    8230, "..." ]
44328 ]; 
44329
44330     //<script type="text/javascript">
44331
44332 /*
44333  * Ext JS Library 1.1.1
44334  * Copyright(c) 2006-2007, Ext JS, LLC.
44335  * Licence LGPL
44336  * 
44337  */
44338  
44339  
44340 Roo.form.HtmlEditor = function(config){
44341     
44342     
44343     
44344     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44345     
44346     if (!this.toolbars) {
44347         this.toolbars = [];
44348     }
44349     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44350     
44351     
44352 };
44353
44354 /**
44355  * @class Roo.form.HtmlEditor
44356  * @extends Roo.form.Field
44357  * Provides a lightweight HTML Editor component.
44358  *
44359  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44360  * 
44361  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44362  * supported by this editor.</b><br/><br/>
44363  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44364  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44365  */
44366 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44367     /**
44368      * @cfg {Boolean} clearUp
44369      */
44370     clearUp : true,
44371       /**
44372      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44373      */
44374     toolbars : false,
44375    
44376      /**
44377      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44378      *                        Roo.resizable.
44379      */
44380     resizable : false,
44381      /**
44382      * @cfg {Number} height (in pixels)
44383      */   
44384     height: 300,
44385    /**
44386      * @cfg {Number} width (in pixels)
44387      */   
44388     width: 500,
44389     
44390     /**
44391      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44392      * 
44393      */
44394     stylesheets: false,
44395     
44396     
44397      /**
44398      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44399      * 
44400      */
44401     cblack: false,
44402     /**
44403      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44404      * 
44405      */
44406     cwhite: false,
44407     
44408      /**
44409      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44410      * 
44411      */
44412     black: false,
44413     /**
44414      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44415      * 
44416      */
44417     white: false,
44418     
44419     // id of frame..
44420     frameId: false,
44421     
44422     // private properties
44423     validationEvent : false,
44424     deferHeight: true,
44425     initialized : false,
44426     activated : false,
44427     
44428     onFocus : Roo.emptyFn,
44429     iframePad:3,
44430     hideMode:'offsets',
44431     
44432     actionMode : 'container', // defaults to hiding it...
44433     
44434     defaultAutoCreate : { // modified by initCompnoent..
44435         tag: "textarea",
44436         style:"width:500px;height:300px;",
44437         autocomplete: "new-password"
44438     },
44439
44440     // private
44441     initComponent : function(){
44442         this.addEvents({
44443             /**
44444              * @event initialize
44445              * Fires when the editor is fully initialized (including the iframe)
44446              * @param {HtmlEditor} this
44447              */
44448             initialize: true,
44449             /**
44450              * @event activate
44451              * Fires when the editor is first receives the focus. Any insertion must wait
44452              * until after this event.
44453              * @param {HtmlEditor} this
44454              */
44455             activate: true,
44456              /**
44457              * @event beforesync
44458              * Fires before the textarea is updated with content from the editor iframe. Return false
44459              * to cancel the sync.
44460              * @param {HtmlEditor} this
44461              * @param {String} html
44462              */
44463             beforesync: true,
44464              /**
44465              * @event beforepush
44466              * Fires before the iframe editor is updated with content from the textarea. Return false
44467              * to cancel the push.
44468              * @param {HtmlEditor} this
44469              * @param {String} html
44470              */
44471             beforepush: true,
44472              /**
44473              * @event sync
44474              * Fires when the textarea is updated with content from the editor iframe.
44475              * @param {HtmlEditor} this
44476              * @param {String} html
44477              */
44478             sync: true,
44479              /**
44480              * @event push
44481              * Fires when the iframe editor is updated with content from the textarea.
44482              * @param {HtmlEditor} this
44483              * @param {String} html
44484              */
44485             push: true,
44486              /**
44487              * @event editmodechange
44488              * Fires when the editor switches edit modes
44489              * @param {HtmlEditor} this
44490              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44491              */
44492             editmodechange: true,
44493             /**
44494              * @event editorevent
44495              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44496              * @param {HtmlEditor} this
44497              */
44498             editorevent: true,
44499             /**
44500              * @event firstfocus
44501              * Fires when on first focus - needed by toolbars..
44502              * @param {HtmlEditor} this
44503              */
44504             firstfocus: true,
44505             /**
44506              * @event autosave
44507              * Auto save the htmlEditor value as a file into Events
44508              * @param {HtmlEditor} this
44509              */
44510             autosave: true,
44511             /**
44512              * @event savedpreview
44513              * preview the saved version of htmlEditor
44514              * @param {HtmlEditor} this
44515              */
44516             savedpreview: true,
44517             
44518             /**
44519             * @event stylesheetsclick
44520             * Fires when press the Sytlesheets button
44521             * @param {Roo.HtmlEditorCore} this
44522             */
44523             stylesheetsclick: true
44524         });
44525         this.defaultAutoCreate =  {
44526             tag: "textarea",
44527             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44528             autocomplete: "new-password"
44529         };
44530     },
44531
44532     /**
44533      * Protected method that will not generally be called directly. It
44534      * is called when the editor creates its toolbar. Override this method if you need to
44535      * add custom toolbar buttons.
44536      * @param {HtmlEditor} editor
44537      */
44538     createToolbar : function(editor){
44539         Roo.log("create toolbars");
44540         if (!editor.toolbars || !editor.toolbars.length) {
44541             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44542         }
44543         
44544         for (var i =0 ; i < editor.toolbars.length;i++) {
44545             editor.toolbars[i] = Roo.factory(
44546                     typeof(editor.toolbars[i]) == 'string' ?
44547                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44548                 Roo.form.HtmlEditor);
44549             editor.toolbars[i].init(editor);
44550         }
44551          
44552         
44553     },
44554
44555      
44556     // private
44557     onRender : function(ct, position)
44558     {
44559         var _t = this;
44560         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44561         
44562         this.wrap = this.el.wrap({
44563             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44564         });
44565         
44566         this.editorcore.onRender(ct, position);
44567          
44568         if (this.resizable) {
44569             this.resizeEl = new Roo.Resizable(this.wrap, {
44570                 pinned : true,
44571                 wrap: true,
44572                 dynamic : true,
44573                 minHeight : this.height,
44574                 height: this.height,
44575                 handles : this.resizable,
44576                 width: this.width,
44577                 listeners : {
44578                     resize : function(r, w, h) {
44579                         _t.onResize(w,h); // -something
44580                     }
44581                 }
44582             });
44583             
44584         }
44585         this.createToolbar(this);
44586        
44587         
44588         if(!this.width){
44589             this.setSize(this.wrap.getSize());
44590         }
44591         if (this.resizeEl) {
44592             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44593             // should trigger onReize..
44594         }
44595         
44596         this.keyNav = new Roo.KeyNav(this.el, {
44597             
44598             "tab" : function(e){
44599                 e.preventDefault();
44600                 
44601                 var value = this.getValue();
44602                 
44603                 var start = this.el.dom.selectionStart;
44604                 var end = this.el.dom.selectionEnd;
44605                 
44606                 if(!e.shiftKey){
44607                     
44608                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44609                     this.el.dom.setSelectionRange(end + 1, end + 1);
44610                     return;
44611                 }
44612                 
44613                 var f = value.substring(0, start).split("\t");
44614                 
44615                 if(f.pop().length != 0){
44616                     return;
44617                 }
44618                 
44619                 this.setValue(f.join("\t") + value.substring(end));
44620                 this.el.dom.setSelectionRange(start - 1, start - 1);
44621                 
44622             },
44623             
44624             "home" : function(e){
44625                 e.preventDefault();
44626                 
44627                 var curr = this.el.dom.selectionStart;
44628                 var lines = this.getValue().split("\n");
44629                 
44630                 if(!lines.length){
44631                     return;
44632                 }
44633                 
44634                 if(e.ctrlKey){
44635                     this.el.dom.setSelectionRange(0, 0);
44636                     return;
44637                 }
44638                 
44639                 var pos = 0;
44640                 
44641                 for (var i = 0; i < lines.length;i++) {
44642                     pos += lines[i].length;
44643                     
44644                     if(i != 0){
44645                         pos += 1;
44646                     }
44647                     
44648                     if(pos < curr){
44649                         continue;
44650                     }
44651                     
44652                     pos -= lines[i].length;
44653                     
44654                     break;
44655                 }
44656                 
44657                 if(!e.shiftKey){
44658                     this.el.dom.setSelectionRange(pos, pos);
44659                     return;
44660                 }
44661                 
44662                 this.el.dom.selectionStart = pos;
44663                 this.el.dom.selectionEnd = curr;
44664             },
44665             
44666             "end" : function(e){
44667                 e.preventDefault();
44668                 
44669                 var curr = this.el.dom.selectionStart;
44670                 var lines = this.getValue().split("\n");
44671                 
44672                 if(!lines.length){
44673                     return;
44674                 }
44675                 
44676                 if(e.ctrlKey){
44677                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44678                     return;
44679                 }
44680                 
44681                 var pos = 0;
44682                 
44683                 for (var i = 0; i < lines.length;i++) {
44684                     
44685                     pos += lines[i].length;
44686                     
44687                     if(i != 0){
44688                         pos += 1;
44689                     }
44690                     
44691                     if(pos < curr){
44692                         continue;
44693                     }
44694                     
44695                     break;
44696                 }
44697                 
44698                 if(!e.shiftKey){
44699                     this.el.dom.setSelectionRange(pos, pos);
44700                     return;
44701                 }
44702                 
44703                 this.el.dom.selectionStart = curr;
44704                 this.el.dom.selectionEnd = pos;
44705             },
44706
44707             scope : this,
44708
44709             doRelay : function(foo, bar, hname){
44710                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44711             },
44712
44713             forceKeyDown: true
44714         });
44715         
44716 //        if(this.autosave && this.w){
44717 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44718 //        }
44719     },
44720
44721     // private
44722     onResize : function(w, h)
44723     {
44724         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44725         var ew = false;
44726         var eh = false;
44727         
44728         if(this.el ){
44729             if(typeof w == 'number'){
44730                 var aw = w - this.wrap.getFrameWidth('lr');
44731                 this.el.setWidth(this.adjustWidth('textarea', aw));
44732                 ew = aw;
44733             }
44734             if(typeof h == 'number'){
44735                 var tbh = 0;
44736                 for (var i =0; i < this.toolbars.length;i++) {
44737                     // fixme - ask toolbars for heights?
44738                     tbh += this.toolbars[i].tb.el.getHeight();
44739                     if (this.toolbars[i].footer) {
44740                         tbh += this.toolbars[i].footer.el.getHeight();
44741                     }
44742                 }
44743                 
44744                 
44745                 
44746                 
44747                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44748                 ah -= 5; // knock a few pixes off for look..
44749 //                Roo.log(ah);
44750                 this.el.setHeight(this.adjustWidth('textarea', ah));
44751                 var eh = ah;
44752             }
44753         }
44754         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44755         this.editorcore.onResize(ew,eh);
44756         
44757     },
44758
44759     /**
44760      * Toggles the editor between standard and source edit mode.
44761      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44762      */
44763     toggleSourceEdit : function(sourceEditMode)
44764     {
44765         this.editorcore.toggleSourceEdit(sourceEditMode);
44766         
44767         if(this.editorcore.sourceEditMode){
44768             Roo.log('editor - showing textarea');
44769             
44770 //            Roo.log('in');
44771 //            Roo.log(this.syncValue());
44772             this.editorcore.syncValue();
44773             this.el.removeClass('x-hidden');
44774             this.el.dom.removeAttribute('tabIndex');
44775             this.el.focus();
44776             
44777             for (var i = 0; i < this.toolbars.length; i++) {
44778                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44779                     this.toolbars[i].tb.hide();
44780                     this.toolbars[i].footer.hide();
44781                 }
44782             }
44783             
44784         }else{
44785             Roo.log('editor - hiding textarea');
44786 //            Roo.log('out')
44787 //            Roo.log(this.pushValue()); 
44788             this.editorcore.pushValue();
44789             
44790             this.el.addClass('x-hidden');
44791             this.el.dom.setAttribute('tabIndex', -1);
44792             
44793             for (var i = 0; i < this.toolbars.length; i++) {
44794                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44795                     this.toolbars[i].tb.show();
44796                     this.toolbars[i].footer.show();
44797                 }
44798             }
44799             
44800             //this.deferFocus();
44801         }
44802         
44803         this.setSize(this.wrap.getSize());
44804         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44805         
44806         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44807     },
44808  
44809     // private (for BoxComponent)
44810     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44811
44812     // private (for BoxComponent)
44813     getResizeEl : function(){
44814         return this.wrap;
44815     },
44816
44817     // private (for BoxComponent)
44818     getPositionEl : function(){
44819         return this.wrap;
44820     },
44821
44822     // private
44823     initEvents : function(){
44824         this.originalValue = this.getValue();
44825     },
44826
44827     /**
44828      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44829      * @method
44830      */
44831     markInvalid : Roo.emptyFn,
44832     /**
44833      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44834      * @method
44835      */
44836     clearInvalid : Roo.emptyFn,
44837
44838     setValue : function(v){
44839         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44840         this.editorcore.pushValue();
44841     },
44842
44843      
44844     // private
44845     deferFocus : function(){
44846         this.focus.defer(10, this);
44847     },
44848
44849     // doc'ed in Field
44850     focus : function(){
44851         this.editorcore.focus();
44852         
44853     },
44854       
44855
44856     // private
44857     onDestroy : function(){
44858         
44859         
44860         
44861         if(this.rendered){
44862             
44863             for (var i =0; i < this.toolbars.length;i++) {
44864                 // fixme - ask toolbars for heights?
44865                 this.toolbars[i].onDestroy();
44866             }
44867             
44868             this.wrap.dom.innerHTML = '';
44869             this.wrap.remove();
44870         }
44871     },
44872
44873     // private
44874     onFirstFocus : function(){
44875         //Roo.log("onFirstFocus");
44876         this.editorcore.onFirstFocus();
44877          for (var i =0; i < this.toolbars.length;i++) {
44878             this.toolbars[i].onFirstFocus();
44879         }
44880         
44881     },
44882     
44883     // private
44884     syncValue : function()
44885     {
44886         this.editorcore.syncValue();
44887     },
44888     
44889     pushValue : function()
44890     {
44891         this.editorcore.pushValue();
44892     },
44893     
44894     setStylesheets : function(stylesheets)
44895     {
44896         this.editorcore.setStylesheets(stylesheets);
44897     },
44898     
44899     removeStylesheets : function()
44900     {
44901         this.editorcore.removeStylesheets();
44902     }
44903      
44904     
44905     // hide stuff that is not compatible
44906     /**
44907      * @event blur
44908      * @hide
44909      */
44910     /**
44911      * @event change
44912      * @hide
44913      */
44914     /**
44915      * @event focus
44916      * @hide
44917      */
44918     /**
44919      * @event specialkey
44920      * @hide
44921      */
44922     /**
44923      * @cfg {String} fieldClass @hide
44924      */
44925     /**
44926      * @cfg {String} focusClass @hide
44927      */
44928     /**
44929      * @cfg {String} autoCreate @hide
44930      */
44931     /**
44932      * @cfg {String} inputType @hide
44933      */
44934     /**
44935      * @cfg {String} invalidClass @hide
44936      */
44937     /**
44938      * @cfg {String} invalidText @hide
44939      */
44940     /**
44941      * @cfg {String} msgFx @hide
44942      */
44943     /**
44944      * @cfg {String} validateOnBlur @hide
44945      */
44946 });
44947  
44948     // <script type="text/javascript">
44949 /*
44950  * Based on
44951  * Ext JS Library 1.1.1
44952  * Copyright(c) 2006-2007, Ext JS, LLC.
44953  *  
44954  
44955  */
44956
44957 /**
44958  * @class Roo.form.HtmlEditorToolbar1
44959  * Basic Toolbar
44960  * 
44961  * Usage:
44962  *
44963  new Roo.form.HtmlEditor({
44964     ....
44965     toolbars : [
44966         new Roo.form.HtmlEditorToolbar1({
44967             disable : { fonts: 1 , format: 1, ..., ... , ...],
44968             btns : [ .... ]
44969         })
44970     }
44971      
44972  * 
44973  * @cfg {Object} disable List of elements to disable..
44974  * @cfg {Array} btns List of additional buttons.
44975  * 
44976  * 
44977  * NEEDS Extra CSS? 
44978  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44979  */
44980  
44981 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44982 {
44983     
44984     Roo.apply(this, config);
44985     
44986     // default disabled, based on 'good practice'..
44987     this.disable = this.disable || {};
44988     Roo.applyIf(this.disable, {
44989         fontSize : true,
44990         colors : true,
44991         specialElements : true
44992     });
44993     
44994     
44995     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44996     // dont call parent... till later.
44997 }
44998
44999 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45000     
45001     tb: false,
45002     
45003     rendered: false,
45004     
45005     editor : false,
45006     editorcore : false,
45007     /**
45008      * @cfg {Object} disable  List of toolbar elements to disable
45009          
45010      */
45011     disable : false,
45012     
45013     
45014      /**
45015      * @cfg {String} createLinkText The default text for the create link prompt
45016      */
45017     createLinkText : 'Please enter the URL for the link:',
45018     /**
45019      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45020      */
45021     defaultLinkValue : 'http:/'+'/',
45022    
45023     
45024       /**
45025      * @cfg {Array} fontFamilies An array of available font families
45026      */
45027     fontFamilies : [
45028         'Arial',
45029         'Courier New',
45030         'Tahoma',
45031         'Times New Roman',
45032         'Verdana'
45033     ],
45034     
45035     specialChars : [
45036            "&#169;",
45037           "&#174;",     
45038           "&#8482;",    
45039           "&#163;" ,    
45040          // "&#8212;",    
45041           "&#8230;",    
45042           "&#247;" ,    
45043         //  "&#225;" ,     ?? a acute?
45044            "&#8364;"    , //Euro
45045        //   "&#8220;"    ,
45046         //  "&#8221;"    ,
45047         //  "&#8226;"    ,
45048           "&#176;"  //   , // degrees
45049
45050          // "&#233;"     , // e ecute
45051          // "&#250;"     , // u ecute?
45052     ],
45053     
45054     specialElements : [
45055         {
45056             text: "Insert Table",
45057             xtype: 'MenuItem',
45058             xns : Roo.Menu,
45059             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45060                 
45061         },
45062         {    
45063             text: "Insert Image",
45064             xtype: 'MenuItem',
45065             xns : Roo.Menu,
45066             ihtml : '<img src="about:blank"/>'
45067             
45068         }
45069         
45070          
45071     ],
45072     
45073     
45074     inputElements : [ 
45075             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45076             "input:submit", "input:button", "select", "textarea", "label" ],
45077     formats : [
45078         ["p"] ,  
45079         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45080         ["pre"],[ "code"], 
45081         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45082         ['div'],['span']
45083     ],
45084     
45085     cleanStyles : [
45086         "font-size"
45087     ],
45088      /**
45089      * @cfg {String} defaultFont default font to use.
45090      */
45091     defaultFont: 'tahoma',
45092    
45093     fontSelect : false,
45094     
45095     
45096     formatCombo : false,
45097     
45098     init : function(editor)
45099     {
45100         this.editor = editor;
45101         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45102         var editorcore = this.editorcore;
45103         
45104         var _t = this;
45105         
45106         var fid = editorcore.frameId;
45107         var etb = this;
45108         function btn(id, toggle, handler){
45109             var xid = fid + '-'+ id ;
45110             return {
45111                 id : xid,
45112                 cmd : id,
45113                 cls : 'x-btn-icon x-edit-'+id,
45114                 enableToggle:toggle !== false,
45115                 scope: _t, // was editor...
45116                 handler:handler||_t.relayBtnCmd,
45117                 clickEvent:'mousedown',
45118                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45119                 tabIndex:-1
45120             };
45121         }
45122         
45123         
45124         
45125         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45126         this.tb = tb;
45127          // stop form submits
45128         tb.el.on('click', function(e){
45129             e.preventDefault(); // what does this do?
45130         });
45131
45132         if(!this.disable.font) { // && !Roo.isSafari){
45133             /* why no safari for fonts 
45134             editor.fontSelect = tb.el.createChild({
45135                 tag:'select',
45136                 tabIndex: -1,
45137                 cls:'x-font-select',
45138                 html: this.createFontOptions()
45139             });
45140             
45141             editor.fontSelect.on('change', function(){
45142                 var font = editor.fontSelect.dom.value;
45143                 editor.relayCmd('fontname', font);
45144                 editor.deferFocus();
45145             }, editor);
45146             
45147             tb.add(
45148                 editor.fontSelect.dom,
45149                 '-'
45150             );
45151             */
45152             
45153         };
45154         if(!this.disable.formats){
45155             this.formatCombo = new Roo.form.ComboBox({
45156                 store: new Roo.data.SimpleStore({
45157                     id : 'tag',
45158                     fields: ['tag'],
45159                     data : this.formats // from states.js
45160                 }),
45161                 blockFocus : true,
45162                 name : '',
45163                 //autoCreate : {tag: "div",  size: "20"},
45164                 displayField:'tag',
45165                 typeAhead: false,
45166                 mode: 'local',
45167                 editable : false,
45168                 triggerAction: 'all',
45169                 emptyText:'Add tag',
45170                 selectOnFocus:true,
45171                 width:135,
45172                 listeners : {
45173                     'select': function(c, r, i) {
45174                         editorcore.insertTag(r.get('tag'));
45175                         editor.focus();
45176                     }
45177                 }
45178
45179             });
45180             tb.addField(this.formatCombo);
45181             
45182         }
45183         
45184         if(!this.disable.format){
45185             tb.add(
45186                 btn('bold'),
45187                 btn('italic'),
45188                 btn('underline'),
45189                 btn('strikethrough')
45190             );
45191         };
45192         if(!this.disable.fontSize){
45193             tb.add(
45194                 '-',
45195                 
45196                 
45197                 btn('increasefontsize', false, editorcore.adjustFont),
45198                 btn('decreasefontsize', false, editorcore.adjustFont)
45199             );
45200         };
45201         
45202         
45203         if(!this.disable.colors){
45204             tb.add(
45205                 '-', {
45206                     id:editorcore.frameId +'-forecolor',
45207                     cls:'x-btn-icon x-edit-forecolor',
45208                     clickEvent:'mousedown',
45209                     tooltip: this.buttonTips['forecolor'] || undefined,
45210                     tabIndex:-1,
45211                     menu : new Roo.menu.ColorMenu({
45212                         allowReselect: true,
45213                         focus: Roo.emptyFn,
45214                         value:'000000',
45215                         plain:true,
45216                         selectHandler: function(cp, color){
45217                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45218                             editor.deferFocus();
45219                         },
45220                         scope: editorcore,
45221                         clickEvent:'mousedown'
45222                     })
45223                 }, {
45224                     id:editorcore.frameId +'backcolor',
45225                     cls:'x-btn-icon x-edit-backcolor',
45226                     clickEvent:'mousedown',
45227                     tooltip: this.buttonTips['backcolor'] || undefined,
45228                     tabIndex:-1,
45229                     menu : new Roo.menu.ColorMenu({
45230                         focus: Roo.emptyFn,
45231                         value:'FFFFFF',
45232                         plain:true,
45233                         allowReselect: true,
45234                         selectHandler: function(cp, color){
45235                             if(Roo.isGecko){
45236                                 editorcore.execCmd('useCSS', false);
45237                                 editorcore.execCmd('hilitecolor', color);
45238                                 editorcore.execCmd('useCSS', true);
45239                                 editor.deferFocus();
45240                             }else{
45241                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45242                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45243                                 editor.deferFocus();
45244                             }
45245                         },
45246                         scope:editorcore,
45247                         clickEvent:'mousedown'
45248                     })
45249                 }
45250             );
45251         };
45252         // now add all the items...
45253         
45254
45255         if(!this.disable.alignments){
45256             tb.add(
45257                 '-',
45258                 btn('justifyleft'),
45259                 btn('justifycenter'),
45260                 btn('justifyright')
45261             );
45262         };
45263
45264         //if(!Roo.isSafari){
45265             if(!this.disable.links){
45266                 tb.add(
45267                     '-',
45268                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45269                 );
45270             };
45271
45272             if(!this.disable.lists){
45273                 tb.add(
45274                     '-',
45275                     btn('insertorderedlist'),
45276                     btn('insertunorderedlist')
45277                 );
45278             }
45279             if(!this.disable.sourceEdit){
45280                 tb.add(
45281                     '-',
45282                     btn('sourceedit', true, function(btn){
45283                         this.toggleSourceEdit(btn.pressed);
45284                     })
45285                 );
45286             }
45287         //}
45288         
45289         var smenu = { };
45290         // special menu.. - needs to be tidied up..
45291         if (!this.disable.special) {
45292             smenu = {
45293                 text: "&#169;",
45294                 cls: 'x-edit-none',
45295                 
45296                 menu : {
45297                     items : []
45298                 }
45299             };
45300             for (var i =0; i < this.specialChars.length; i++) {
45301                 smenu.menu.items.push({
45302                     
45303                     html: this.specialChars[i],
45304                     handler: function(a,b) {
45305                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45306                         //editor.insertAtCursor(a.html);
45307                         
45308                     },
45309                     tabIndex:-1
45310                 });
45311             }
45312             
45313             
45314             tb.add(smenu);
45315             
45316             
45317         }
45318         
45319         var cmenu = { };
45320         if (!this.disable.cleanStyles) {
45321             cmenu = {
45322                 cls: 'x-btn-icon x-btn-clear',
45323                 
45324                 menu : {
45325                     items : []
45326                 }
45327             };
45328             for (var i =0; i < this.cleanStyles.length; i++) {
45329                 cmenu.menu.items.push({
45330                     actiontype : this.cleanStyles[i],
45331                     html: 'Remove ' + this.cleanStyles[i],
45332                     handler: function(a,b) {
45333 //                        Roo.log(a);
45334 //                        Roo.log(b);
45335                         var c = Roo.get(editorcore.doc.body);
45336                         c.select('[style]').each(function(s) {
45337                             s.dom.style.removeProperty(a.actiontype);
45338                         });
45339                         editorcore.syncValue();
45340                     },
45341                     tabIndex:-1
45342                 });
45343             }
45344              cmenu.menu.items.push({
45345                 actiontype : 'tablewidths',
45346                 html: 'Remove Table Widths',
45347                 handler: function(a,b) {
45348                     editorcore.cleanTableWidths();
45349                     editorcore.syncValue();
45350                 },
45351                 tabIndex:-1
45352             });
45353             cmenu.menu.items.push({
45354                 actiontype : 'word',
45355                 html: 'Remove MS Word Formating',
45356                 handler: function(a,b) {
45357                     editorcore.cleanWord();
45358                     editorcore.syncValue();
45359                 },
45360                 tabIndex:-1
45361             });
45362             
45363             cmenu.menu.items.push({
45364                 actiontype : 'all',
45365                 html: 'Remove All Styles',
45366                 handler: function(a,b) {
45367                     
45368                     var c = Roo.get(editorcore.doc.body);
45369                     c.select('[style]').each(function(s) {
45370                         s.dom.removeAttribute('style');
45371                     });
45372                     editorcore.syncValue();
45373                 },
45374                 tabIndex:-1
45375             });
45376             
45377             cmenu.menu.items.push({
45378                 actiontype : 'all',
45379                 html: 'Remove All CSS Classes',
45380                 handler: function(a,b) {
45381                     
45382                     var c = Roo.get(editorcore.doc.body);
45383                     c.select('[class]').each(function(s) {
45384                         s.dom.className = '';
45385                     });
45386                     editorcore.syncValue();
45387                 },
45388                 tabIndex:-1
45389             });
45390             
45391              cmenu.menu.items.push({
45392                 actiontype : 'tidy',
45393                 html: 'Tidy HTML Source',
45394                 handler: function(a,b) {
45395                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45396                     editorcore.syncValue();
45397                 },
45398                 tabIndex:-1
45399             });
45400             
45401             
45402             tb.add(cmenu);
45403         }
45404          
45405         if (!this.disable.specialElements) {
45406             var semenu = {
45407                 text: "Other;",
45408                 cls: 'x-edit-none',
45409                 menu : {
45410                     items : []
45411                 }
45412             };
45413             for (var i =0; i < this.specialElements.length; i++) {
45414                 semenu.menu.items.push(
45415                     Roo.apply({ 
45416                         handler: function(a,b) {
45417                             editor.insertAtCursor(this.ihtml);
45418                         }
45419                     }, this.specialElements[i])
45420                 );
45421                     
45422             }
45423             
45424             tb.add(semenu);
45425             
45426             
45427         }
45428          
45429         
45430         if (this.btns) {
45431             for(var i =0; i< this.btns.length;i++) {
45432                 var b = Roo.factory(this.btns[i],Roo.form);
45433                 b.cls =  'x-edit-none';
45434                 
45435                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45436                     b.cls += ' x-init-enable';
45437                 }
45438                 
45439                 b.scope = editorcore;
45440                 tb.add(b);
45441             }
45442         
45443         }
45444         
45445         
45446         
45447         // disable everything...
45448         
45449         this.tb.items.each(function(item){
45450             
45451            if(
45452                 item.id != editorcore.frameId+ '-sourceedit' && 
45453                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45454             ){
45455                 
45456                 item.disable();
45457             }
45458         });
45459         this.rendered = true;
45460         
45461         // the all the btns;
45462         editor.on('editorevent', this.updateToolbar, this);
45463         // other toolbars need to implement this..
45464         //editor.on('editmodechange', this.updateToolbar, this);
45465     },
45466     
45467     
45468     relayBtnCmd : function(btn) {
45469         this.editorcore.relayCmd(btn.cmd);
45470     },
45471     // private used internally
45472     createLink : function(){
45473         Roo.log("create link?");
45474         var url = prompt(this.createLinkText, this.defaultLinkValue);
45475         if(url && url != 'http:/'+'/'){
45476             this.editorcore.relayCmd('createlink', url);
45477         }
45478     },
45479
45480     
45481     /**
45482      * Protected method that will not generally be called directly. It triggers
45483      * a toolbar update by reading the markup state of the current selection in the editor.
45484      */
45485     updateToolbar: function(){
45486
45487         if(!this.editorcore.activated){
45488             this.editor.onFirstFocus();
45489             return;
45490         }
45491
45492         var btns = this.tb.items.map, 
45493             doc = this.editorcore.doc,
45494             frameId = this.editorcore.frameId;
45495
45496         if(!this.disable.font && !Roo.isSafari){
45497             /*
45498             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45499             if(name != this.fontSelect.dom.value){
45500                 this.fontSelect.dom.value = name;
45501             }
45502             */
45503         }
45504         if(!this.disable.format){
45505             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45506             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45507             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45508             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45509         }
45510         if(!this.disable.alignments){
45511             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45512             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45513             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45514         }
45515         if(!Roo.isSafari && !this.disable.lists){
45516             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45517             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45518         }
45519         
45520         var ans = this.editorcore.getAllAncestors();
45521         if (this.formatCombo) {
45522             
45523             
45524             var store = this.formatCombo.store;
45525             this.formatCombo.setValue("");
45526             for (var i =0; i < ans.length;i++) {
45527                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45528                     // select it..
45529                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45530                     break;
45531                 }
45532             }
45533         }
45534         
45535         
45536         
45537         // hides menus... - so this cant be on a menu...
45538         Roo.menu.MenuMgr.hideAll();
45539
45540         //this.editorsyncValue();
45541     },
45542    
45543     
45544     createFontOptions : function(){
45545         var buf = [], fs = this.fontFamilies, ff, lc;
45546         
45547         
45548         
45549         for(var i = 0, len = fs.length; i< len; i++){
45550             ff = fs[i];
45551             lc = ff.toLowerCase();
45552             buf.push(
45553                 '<option value="',lc,'" style="font-family:',ff,';"',
45554                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45555                     ff,
45556                 '</option>'
45557             );
45558         }
45559         return buf.join('');
45560     },
45561     
45562     toggleSourceEdit : function(sourceEditMode){
45563         
45564         Roo.log("toolbar toogle");
45565         if(sourceEditMode === undefined){
45566             sourceEditMode = !this.sourceEditMode;
45567         }
45568         this.sourceEditMode = sourceEditMode === true;
45569         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45570         // just toggle the button?
45571         if(btn.pressed !== this.sourceEditMode){
45572             btn.toggle(this.sourceEditMode);
45573             return;
45574         }
45575         
45576         if(sourceEditMode){
45577             Roo.log("disabling buttons");
45578             this.tb.items.each(function(item){
45579                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45580                     item.disable();
45581                 }
45582             });
45583           
45584         }else{
45585             Roo.log("enabling buttons");
45586             if(this.editorcore.initialized){
45587                 this.tb.items.each(function(item){
45588                     item.enable();
45589                 });
45590             }
45591             
45592         }
45593         Roo.log("calling toggole on editor");
45594         // tell the editor that it's been pressed..
45595         this.editor.toggleSourceEdit(sourceEditMode);
45596        
45597     },
45598      /**
45599      * Object collection of toolbar tooltips for the buttons in the editor. The key
45600      * is the command id associated with that button and the value is a valid QuickTips object.
45601      * For example:
45602 <pre><code>
45603 {
45604     bold : {
45605         title: 'Bold (Ctrl+B)',
45606         text: 'Make the selected text bold.',
45607         cls: 'x-html-editor-tip'
45608     },
45609     italic : {
45610         title: 'Italic (Ctrl+I)',
45611         text: 'Make the selected text italic.',
45612         cls: 'x-html-editor-tip'
45613     },
45614     ...
45615 </code></pre>
45616     * @type Object
45617      */
45618     buttonTips : {
45619         bold : {
45620             title: 'Bold (Ctrl+B)',
45621             text: 'Make the selected text bold.',
45622             cls: 'x-html-editor-tip'
45623         },
45624         italic : {
45625             title: 'Italic (Ctrl+I)',
45626             text: 'Make the selected text italic.',
45627             cls: 'x-html-editor-tip'
45628         },
45629         underline : {
45630             title: 'Underline (Ctrl+U)',
45631             text: 'Underline the selected text.',
45632             cls: 'x-html-editor-tip'
45633         },
45634         strikethrough : {
45635             title: 'Strikethrough',
45636             text: 'Strikethrough the selected text.',
45637             cls: 'x-html-editor-tip'
45638         },
45639         increasefontsize : {
45640             title: 'Grow Text',
45641             text: 'Increase the font size.',
45642             cls: 'x-html-editor-tip'
45643         },
45644         decreasefontsize : {
45645             title: 'Shrink Text',
45646             text: 'Decrease the font size.',
45647             cls: 'x-html-editor-tip'
45648         },
45649         backcolor : {
45650             title: 'Text Highlight Color',
45651             text: 'Change the background color of the selected text.',
45652             cls: 'x-html-editor-tip'
45653         },
45654         forecolor : {
45655             title: 'Font Color',
45656             text: 'Change the color of the selected text.',
45657             cls: 'x-html-editor-tip'
45658         },
45659         justifyleft : {
45660             title: 'Align Text Left',
45661             text: 'Align text to the left.',
45662             cls: 'x-html-editor-tip'
45663         },
45664         justifycenter : {
45665             title: 'Center Text',
45666             text: 'Center text in the editor.',
45667             cls: 'x-html-editor-tip'
45668         },
45669         justifyright : {
45670             title: 'Align Text Right',
45671             text: 'Align text to the right.',
45672             cls: 'x-html-editor-tip'
45673         },
45674         insertunorderedlist : {
45675             title: 'Bullet List',
45676             text: 'Start a bulleted list.',
45677             cls: 'x-html-editor-tip'
45678         },
45679         insertorderedlist : {
45680             title: 'Numbered List',
45681             text: 'Start a numbered list.',
45682             cls: 'x-html-editor-tip'
45683         },
45684         createlink : {
45685             title: 'Hyperlink',
45686             text: 'Make the selected text a hyperlink.',
45687             cls: 'x-html-editor-tip'
45688         },
45689         sourceedit : {
45690             title: 'Source Edit',
45691             text: 'Switch to source editing mode.',
45692             cls: 'x-html-editor-tip'
45693         }
45694     },
45695     // private
45696     onDestroy : function(){
45697         if(this.rendered){
45698             
45699             this.tb.items.each(function(item){
45700                 if(item.menu){
45701                     item.menu.removeAll();
45702                     if(item.menu.el){
45703                         item.menu.el.destroy();
45704                     }
45705                 }
45706                 item.destroy();
45707             });
45708              
45709         }
45710     },
45711     onFirstFocus: function() {
45712         this.tb.items.each(function(item){
45713            item.enable();
45714         });
45715     }
45716 });
45717
45718
45719
45720
45721 // <script type="text/javascript">
45722 /*
45723  * Based on
45724  * Ext JS Library 1.1.1
45725  * Copyright(c) 2006-2007, Ext JS, LLC.
45726  *  
45727  
45728  */
45729
45730  
45731 /**
45732  * @class Roo.form.HtmlEditor.ToolbarContext
45733  * Context Toolbar
45734  * 
45735  * Usage:
45736  *
45737  new Roo.form.HtmlEditor({
45738     ....
45739     toolbars : [
45740         { xtype: 'ToolbarStandard', styles : {} }
45741         { xtype: 'ToolbarContext', disable : {} }
45742     ]
45743 })
45744
45745      
45746  * 
45747  * @config : {Object} disable List of elements to disable.. (not done yet.)
45748  * @config : {Object} styles  Map of styles available.
45749  * 
45750  */
45751
45752 Roo.form.HtmlEditor.ToolbarContext = function(config)
45753 {
45754     
45755     Roo.apply(this, config);
45756     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45757     // dont call parent... till later.
45758     this.styles = this.styles || {};
45759 }
45760
45761  
45762
45763 Roo.form.HtmlEditor.ToolbarContext.types = {
45764     'IMG' : {
45765         width : {
45766             title: "Width",
45767             width: 40
45768         },
45769         height:  {
45770             title: "Height",
45771             width: 40
45772         },
45773         align: {
45774             title: "Align",
45775             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45776             width : 80
45777             
45778         },
45779         border: {
45780             title: "Border",
45781             width: 40
45782         },
45783         alt: {
45784             title: "Alt",
45785             width: 120
45786         },
45787         src : {
45788             title: "Src",
45789             width: 220
45790         }
45791         
45792     },
45793     'A' : {
45794         name : {
45795             title: "Name",
45796             width: 50
45797         },
45798         target:  {
45799             title: "Target",
45800             width: 120
45801         },
45802         href:  {
45803             title: "Href",
45804             width: 220
45805         } // border?
45806         
45807     },
45808     'TABLE' : {
45809         rows : {
45810             title: "Rows",
45811             width: 20
45812         },
45813         cols : {
45814             title: "Cols",
45815             width: 20
45816         },
45817         width : {
45818             title: "Width",
45819             width: 40
45820         },
45821         height : {
45822             title: "Height",
45823             width: 40
45824         },
45825         border : {
45826             title: "Border",
45827             width: 20
45828         }
45829     },
45830     'TD' : {
45831         width : {
45832             title: "Width",
45833             width: 40
45834         },
45835         height : {
45836             title: "Height",
45837             width: 40
45838         },   
45839         align: {
45840             title: "Align",
45841             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45842             width: 80
45843         },
45844         valign: {
45845             title: "Valign",
45846             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45847             width: 80
45848         },
45849         colspan: {
45850             title: "Colspan",
45851             width: 20
45852             
45853         },
45854          'font-family'  : {
45855             title : "Font",
45856             style : 'fontFamily',
45857             displayField: 'display',
45858             optname : 'font-family',
45859             width: 140
45860         }
45861     },
45862     'INPUT' : {
45863         name : {
45864             title: "name",
45865             width: 120
45866         },
45867         value : {
45868             title: "Value",
45869             width: 120
45870         },
45871         width : {
45872             title: "Width",
45873             width: 40
45874         }
45875     },
45876     'LABEL' : {
45877         'for' : {
45878             title: "For",
45879             width: 120
45880         }
45881     },
45882     'TEXTAREA' : {
45883           name : {
45884             title: "name",
45885             width: 120
45886         },
45887         rows : {
45888             title: "Rows",
45889             width: 20
45890         },
45891         cols : {
45892             title: "Cols",
45893             width: 20
45894         }
45895     },
45896     'SELECT' : {
45897         name : {
45898             title: "name",
45899             width: 120
45900         },
45901         selectoptions : {
45902             title: "Options",
45903             width: 200
45904         }
45905     },
45906     
45907     // should we really allow this??
45908     // should this just be 
45909     'BODY' : {
45910         title : {
45911             title: "Title",
45912             width: 200,
45913             disabled : true
45914         }
45915     },
45916     'SPAN' : {
45917         'font-family'  : {
45918             title : "Font",
45919             style : 'fontFamily',
45920             displayField: 'display',
45921             optname : 'font-family',
45922             width: 140
45923         }
45924     },
45925     'DIV' : {
45926         'font-family'  : {
45927             title : "Font",
45928             style : 'fontFamily',
45929             displayField: 'display',
45930             optname : 'font-family',
45931             width: 140
45932         }
45933     },
45934      'P' : {
45935         'font-family'  : {
45936             title : "Font",
45937             style : 'fontFamily',
45938             displayField: 'display',
45939             optname : 'font-family',
45940             width: 140
45941         }
45942     },
45943     
45944     '*' : {
45945         // empty..
45946     }
45947
45948 };
45949
45950 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45951 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45952
45953 Roo.form.HtmlEditor.ToolbarContext.options = {
45954         'font-family'  : [ 
45955                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45956                 [ 'Courier New', 'Courier New'],
45957                 [ 'Tahoma', 'Tahoma'],
45958                 [ 'Times New Roman,serif', 'Times'],
45959                 [ 'Verdana','Verdana' ]
45960         ]
45961 };
45962
45963 // fixme - these need to be configurable..
45964  
45965
45966 //Roo.form.HtmlEditor.ToolbarContext.types
45967
45968
45969 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45970     
45971     tb: false,
45972     
45973     rendered: false,
45974     
45975     editor : false,
45976     editorcore : false,
45977     /**
45978      * @cfg {Object} disable  List of toolbar elements to disable
45979          
45980      */
45981     disable : false,
45982     /**
45983      * @cfg {Object} styles List of styles 
45984      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45985      *
45986      * These must be defined in the page, so they get rendered correctly..
45987      * .headline { }
45988      * TD.underline { }
45989      * 
45990      */
45991     styles : false,
45992     
45993     options: false,
45994     
45995     toolbars : false,
45996     
45997     init : function(editor)
45998     {
45999         this.editor = editor;
46000         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46001         var editorcore = this.editorcore;
46002         
46003         var fid = editorcore.frameId;
46004         var etb = this;
46005         function btn(id, toggle, handler){
46006             var xid = fid + '-'+ id ;
46007             return {
46008                 id : xid,
46009                 cmd : id,
46010                 cls : 'x-btn-icon x-edit-'+id,
46011                 enableToggle:toggle !== false,
46012                 scope: editorcore, // was editor...
46013                 handler:handler||editorcore.relayBtnCmd,
46014                 clickEvent:'mousedown',
46015                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46016                 tabIndex:-1
46017             };
46018         }
46019         // create a new element.
46020         var wdiv = editor.wrap.createChild({
46021                 tag: 'div'
46022             }, editor.wrap.dom.firstChild.nextSibling, true);
46023         
46024         // can we do this more than once??
46025         
46026          // stop form submits
46027       
46028  
46029         // disable everything...
46030         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46031         this.toolbars = {};
46032            
46033         for (var i in  ty) {
46034           
46035             this.toolbars[i] = this.buildToolbar(ty[i],i);
46036         }
46037         this.tb = this.toolbars.BODY;
46038         this.tb.el.show();
46039         this.buildFooter();
46040         this.footer.show();
46041         editor.on('hide', function( ) { this.footer.hide() }, this);
46042         editor.on('show', function( ) { this.footer.show() }, this);
46043         
46044          
46045         this.rendered = true;
46046         
46047         // the all the btns;
46048         editor.on('editorevent', this.updateToolbar, this);
46049         // other toolbars need to implement this..
46050         //editor.on('editmodechange', this.updateToolbar, this);
46051     },
46052     
46053     
46054     
46055     /**
46056      * Protected method that will not generally be called directly. It triggers
46057      * a toolbar update by reading the markup state of the current selection in the editor.
46058      *
46059      * Note you can force an update by calling on('editorevent', scope, false)
46060      */
46061     updateToolbar: function(editor,ev,sel){
46062
46063         //Roo.log(ev);
46064         // capture mouse up - this is handy for selecting images..
46065         // perhaps should go somewhere else...
46066         if(!this.editorcore.activated){
46067              this.editor.onFirstFocus();
46068             return;
46069         }
46070         
46071         
46072         
46073         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46074         // selectNode - might want to handle IE?
46075         if (ev &&
46076             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46077             ev.target && ev.target.tagName == 'IMG') {
46078             // they have click on an image...
46079             // let's see if we can change the selection...
46080             sel = ev.target;
46081          
46082               var nodeRange = sel.ownerDocument.createRange();
46083             try {
46084                 nodeRange.selectNode(sel);
46085             } catch (e) {
46086                 nodeRange.selectNodeContents(sel);
46087             }
46088             //nodeRange.collapse(true);
46089             var s = this.editorcore.win.getSelection();
46090             s.removeAllRanges();
46091             s.addRange(nodeRange);
46092         }  
46093         
46094       
46095         var updateFooter = sel ? false : true;
46096         
46097         
46098         var ans = this.editorcore.getAllAncestors();
46099         
46100         // pick
46101         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46102         
46103         if (!sel) { 
46104             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46105             sel = sel ? sel : this.editorcore.doc.body;
46106             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46107             
46108         }
46109         // pick a menu that exists..
46110         var tn = sel.tagName.toUpperCase();
46111         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46112         
46113         tn = sel.tagName.toUpperCase();
46114         
46115         var lastSel = this.tb.selectedNode;
46116         
46117         this.tb.selectedNode = sel;
46118         
46119         // if current menu does not match..
46120         
46121         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46122                 
46123             this.tb.el.hide();
46124             ///console.log("show: " + tn);
46125             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46126             this.tb.el.show();
46127             // update name
46128             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46129             
46130             
46131             // update attributes
46132             if (this.tb.fields) {
46133                 this.tb.fields.each(function(e) {
46134                     if (e.stylename) {
46135                         e.setValue(sel.style[e.stylename]);
46136                         return;
46137                     } 
46138                    e.setValue(sel.getAttribute(e.attrname));
46139                 });
46140             }
46141             
46142             var hasStyles = false;
46143             for(var i in this.styles) {
46144                 hasStyles = true;
46145                 break;
46146             }
46147             
46148             // update styles
46149             if (hasStyles) { 
46150                 var st = this.tb.fields.item(0);
46151                 
46152                 st.store.removeAll();
46153                
46154                 
46155                 var cn = sel.className.split(/\s+/);
46156                 
46157                 var avs = [];
46158                 if (this.styles['*']) {
46159                     
46160                     Roo.each(this.styles['*'], function(v) {
46161                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46162                     });
46163                 }
46164                 if (this.styles[tn]) { 
46165                     Roo.each(this.styles[tn], function(v) {
46166                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46167                     });
46168                 }
46169                 
46170                 st.store.loadData(avs);
46171                 st.collapse();
46172                 st.setValue(cn);
46173             }
46174             // flag our selected Node.
46175             this.tb.selectedNode = sel;
46176            
46177            
46178             Roo.menu.MenuMgr.hideAll();
46179
46180         }
46181         
46182         if (!updateFooter) {
46183             //this.footDisp.dom.innerHTML = ''; 
46184             return;
46185         }
46186         // update the footer
46187         //
46188         var html = '';
46189         
46190         this.footerEls = ans.reverse();
46191         Roo.each(this.footerEls, function(a,i) {
46192             if (!a) { return; }
46193             html += html.length ? ' &gt; '  :  '';
46194             
46195             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46196             
46197         });
46198        
46199         // 
46200         var sz = this.footDisp.up('td').getSize();
46201         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46202         this.footDisp.dom.style.marginLeft = '5px';
46203         
46204         this.footDisp.dom.style.overflow = 'hidden';
46205         
46206         this.footDisp.dom.innerHTML = html;
46207             
46208         //this.editorsyncValue();
46209     },
46210      
46211     
46212    
46213        
46214     // private
46215     onDestroy : function(){
46216         if(this.rendered){
46217             
46218             this.tb.items.each(function(item){
46219                 if(item.menu){
46220                     item.menu.removeAll();
46221                     if(item.menu.el){
46222                         item.menu.el.destroy();
46223                     }
46224                 }
46225                 item.destroy();
46226             });
46227              
46228         }
46229     },
46230     onFirstFocus: function() {
46231         // need to do this for all the toolbars..
46232         this.tb.items.each(function(item){
46233            item.enable();
46234         });
46235     },
46236     buildToolbar: function(tlist, nm)
46237     {
46238         var editor = this.editor;
46239         var editorcore = this.editorcore;
46240          // create a new element.
46241         var wdiv = editor.wrap.createChild({
46242                 tag: 'div'
46243             }, editor.wrap.dom.firstChild.nextSibling, true);
46244         
46245        
46246         var tb = new Roo.Toolbar(wdiv);
46247         // add the name..
46248         
46249         tb.add(nm+ ":&nbsp;");
46250         
46251         var styles = [];
46252         for(var i in this.styles) {
46253             styles.push(i);
46254         }
46255         
46256         // styles...
46257         if (styles && styles.length) {
46258             
46259             // this needs a multi-select checkbox...
46260             tb.addField( new Roo.form.ComboBox({
46261                 store: new Roo.data.SimpleStore({
46262                     id : 'val',
46263                     fields: ['val', 'selected'],
46264                     data : [] 
46265                 }),
46266                 name : '-roo-edit-className',
46267                 attrname : 'className',
46268                 displayField: 'val',
46269                 typeAhead: false,
46270                 mode: 'local',
46271                 editable : false,
46272                 triggerAction: 'all',
46273                 emptyText:'Select Style',
46274                 selectOnFocus:true,
46275                 width: 130,
46276                 listeners : {
46277                     'select': function(c, r, i) {
46278                         // initial support only for on class per el..
46279                         tb.selectedNode.className =  r ? r.get('val') : '';
46280                         editorcore.syncValue();
46281                     }
46282                 }
46283     
46284             }));
46285         }
46286         
46287         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46288         var tbops = tbc.options;
46289         
46290         for (var i in tlist) {
46291             
46292             var item = tlist[i];
46293             tb.add(item.title + ":&nbsp;");
46294             
46295             
46296             //optname == used so you can configure the options available..
46297             var opts = item.opts ? item.opts : false;
46298             if (item.optname) {
46299                 opts = tbops[item.optname];
46300            
46301             }
46302             
46303             if (opts) {
46304                 // opts == pulldown..
46305                 tb.addField( new Roo.form.ComboBox({
46306                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46307                         id : 'val',
46308                         fields: ['val', 'display'],
46309                         data : opts  
46310                     }),
46311                     name : '-roo-edit-' + i,
46312                     attrname : i,
46313                     stylename : item.style ? item.style : false,
46314                     displayField: item.displayField ? item.displayField : 'val',
46315                     valueField :  'val',
46316                     typeAhead: false,
46317                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46318                     editable : false,
46319                     triggerAction: 'all',
46320                     emptyText:'Select',
46321                     selectOnFocus:true,
46322                     width: item.width ? item.width  : 130,
46323                     listeners : {
46324                         'select': function(c, r, i) {
46325                             if (c.stylename) {
46326                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46327                                 return;
46328                             }
46329                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46330                         }
46331                     }
46332
46333                 }));
46334                 continue;
46335                     
46336                  
46337                 
46338                 tb.addField( new Roo.form.TextField({
46339                     name: i,
46340                     width: 100,
46341                     //allowBlank:false,
46342                     value: ''
46343                 }));
46344                 continue;
46345             }
46346             tb.addField( new Roo.form.TextField({
46347                 name: '-roo-edit-' + i,
46348                 attrname : i,
46349                 
46350                 width: item.width,
46351                 //allowBlank:true,
46352                 value: '',
46353                 listeners: {
46354                     'change' : function(f, nv, ov) {
46355                         tb.selectedNode.setAttribute(f.attrname, nv);
46356                         editorcore.syncValue();
46357                     }
46358                 }
46359             }));
46360              
46361         }
46362         
46363         var _this = this;
46364         
46365         if(nm == 'BODY'){
46366             tb.addSeparator();
46367         
46368             tb.addButton( {
46369                 text: 'Stylesheets',
46370
46371                 listeners : {
46372                     click : function ()
46373                     {
46374                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46375                     }
46376                 }
46377             });
46378         }
46379         
46380         tb.addFill();
46381         tb.addButton( {
46382             text: 'Remove Tag',
46383     
46384             listeners : {
46385                 click : function ()
46386                 {
46387                     // remove
46388                     // undo does not work.
46389                      
46390                     var sn = tb.selectedNode;
46391                     
46392                     var pn = sn.parentNode;
46393                     
46394                     var stn =  sn.childNodes[0];
46395                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46396                     while (sn.childNodes.length) {
46397                         var node = sn.childNodes[0];
46398                         sn.removeChild(node);
46399                         //Roo.log(node);
46400                         pn.insertBefore(node, sn);
46401                         
46402                     }
46403                     pn.removeChild(sn);
46404                     var range = editorcore.createRange();
46405         
46406                     range.setStart(stn,0);
46407                     range.setEnd(en,0); //????
46408                     //range.selectNode(sel);
46409                     
46410                     
46411                     var selection = editorcore.getSelection();
46412                     selection.removeAllRanges();
46413                     selection.addRange(range);
46414                     
46415                     
46416                     
46417                     //_this.updateToolbar(null, null, pn);
46418                     _this.updateToolbar(null, null, null);
46419                     _this.footDisp.dom.innerHTML = ''; 
46420                 }
46421             }
46422             
46423                     
46424                 
46425             
46426         });
46427         
46428         
46429         tb.el.on('click', function(e){
46430             e.preventDefault(); // what does this do?
46431         });
46432         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46433         tb.el.hide();
46434         tb.name = nm;
46435         // dont need to disable them... as they will get hidden
46436         return tb;
46437          
46438         
46439     },
46440     buildFooter : function()
46441     {
46442         
46443         var fel = this.editor.wrap.createChild();
46444         this.footer = new Roo.Toolbar(fel);
46445         // toolbar has scrolly on left / right?
46446         var footDisp= new Roo.Toolbar.Fill();
46447         var _t = this;
46448         this.footer.add(
46449             {
46450                 text : '&lt;',
46451                 xtype: 'Button',
46452                 handler : function() {
46453                     _t.footDisp.scrollTo('left',0,true)
46454                 }
46455             }
46456         );
46457         this.footer.add( footDisp );
46458         this.footer.add( 
46459             {
46460                 text : '&gt;',
46461                 xtype: 'Button',
46462                 handler : function() {
46463                     // no animation..
46464                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46465                 }
46466             }
46467         );
46468         var fel = Roo.get(footDisp.el);
46469         fel.addClass('x-editor-context');
46470         this.footDispWrap = fel; 
46471         this.footDispWrap.overflow  = 'hidden';
46472         
46473         this.footDisp = fel.createChild();
46474         this.footDispWrap.on('click', this.onContextClick, this)
46475         
46476         
46477     },
46478     onContextClick : function (ev,dom)
46479     {
46480         ev.preventDefault();
46481         var  cn = dom.className;
46482         //Roo.log(cn);
46483         if (!cn.match(/x-ed-loc-/)) {
46484             return;
46485         }
46486         var n = cn.split('-').pop();
46487         var ans = this.footerEls;
46488         var sel = ans[n];
46489         
46490          // pick
46491         var range = this.editorcore.createRange();
46492         
46493         range.selectNodeContents(sel);
46494         //range.selectNode(sel);
46495         
46496         
46497         var selection = this.editorcore.getSelection();
46498         selection.removeAllRanges();
46499         selection.addRange(range);
46500         
46501         
46502         
46503         this.updateToolbar(null, null, sel);
46504         
46505         
46506     }
46507     
46508     
46509     
46510     
46511     
46512 });
46513
46514
46515
46516
46517
46518 /*
46519  * Based on:
46520  * Ext JS Library 1.1.1
46521  * Copyright(c) 2006-2007, Ext JS, LLC.
46522  *
46523  * Originally Released Under LGPL - original licence link has changed is not relivant.
46524  *
46525  * Fork - LGPL
46526  * <script type="text/javascript">
46527  */
46528  
46529 /**
46530  * @class Roo.form.BasicForm
46531  * @extends Roo.util.Observable
46532  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46533  * @constructor
46534  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46535  * @param {Object} config Configuration options
46536  */
46537 Roo.form.BasicForm = function(el, config){
46538     this.allItems = [];
46539     this.childForms = [];
46540     Roo.apply(this, config);
46541     /*
46542      * The Roo.form.Field items in this form.
46543      * @type MixedCollection
46544      */
46545      
46546      
46547     this.items = new Roo.util.MixedCollection(false, function(o){
46548         return o.id || (o.id = Roo.id());
46549     });
46550     this.addEvents({
46551         /**
46552          * @event beforeaction
46553          * Fires before any action is performed. Return false to cancel the action.
46554          * @param {Form} this
46555          * @param {Action} action The action to be performed
46556          */
46557         beforeaction: true,
46558         /**
46559          * @event actionfailed
46560          * Fires when an action fails.
46561          * @param {Form} this
46562          * @param {Action} action The action that failed
46563          */
46564         actionfailed : true,
46565         /**
46566          * @event actioncomplete
46567          * Fires when an action is completed.
46568          * @param {Form} this
46569          * @param {Action} action The action that completed
46570          */
46571         actioncomplete : true
46572     });
46573     if(el){
46574         this.initEl(el);
46575     }
46576     Roo.form.BasicForm.superclass.constructor.call(this);
46577 };
46578
46579 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46580     /**
46581      * @cfg {String} method
46582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46583      */
46584     /**
46585      * @cfg {DataReader} reader
46586      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46587      * This is optional as there is built-in support for processing JSON.
46588      */
46589     /**
46590      * @cfg {DataReader} errorReader
46591      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46592      * This is completely optional as there is built-in support for processing JSON.
46593      */
46594     /**
46595      * @cfg {String} url
46596      * The URL to use for form actions if one isn't supplied in the action options.
46597      */
46598     /**
46599      * @cfg {Boolean} fileUpload
46600      * Set to true if this form is a file upload.
46601      */
46602      
46603     /**
46604      * @cfg {Object} baseParams
46605      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46606      */
46607      /**
46608      
46609     /**
46610      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46611      */
46612     timeout: 30,
46613
46614     // private
46615     activeAction : null,
46616
46617     /**
46618      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46619      * or setValues() data instead of when the form was first created.
46620      */
46621     trackResetOnLoad : false,
46622     
46623     
46624     /**
46625      * childForms - used for multi-tab forms
46626      * @type {Array}
46627      */
46628     childForms : false,
46629     
46630     /**
46631      * allItems - full list of fields.
46632      * @type {Array}
46633      */
46634     allItems : false,
46635     
46636     /**
46637      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46638      * element by passing it or its id or mask the form itself by passing in true.
46639      * @type Mixed
46640      */
46641     waitMsgTarget : false,
46642
46643     // private
46644     initEl : function(el){
46645         this.el = Roo.get(el);
46646         this.id = this.el.id || Roo.id();
46647         this.el.on('submit', this.onSubmit, this);
46648         this.el.addClass('x-form');
46649     },
46650
46651     // private
46652     onSubmit : function(e){
46653         e.stopEvent();
46654     },
46655
46656     /**
46657      * Returns true if client-side validation on the form is successful.
46658      * @return Boolean
46659      */
46660     isValid : function(){
46661         var valid = true;
46662         this.items.each(function(f){
46663            if(!f.validate()){
46664                valid = false;
46665            }
46666         });
46667         return valid;
46668     },
46669
46670     /**
46671      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46672      * @return Boolean
46673      */
46674     isDirty : function(){
46675         var dirty = false;
46676         this.items.each(function(f){
46677            if(f.isDirty()){
46678                dirty = true;
46679                return false;
46680            }
46681         });
46682         return dirty;
46683     },
46684     
46685     /**
46686      * Returns true if any fields in this form have changed since their original load. (New version)
46687      * @return Boolean
46688      */
46689     
46690     hasChanged : function()
46691     {
46692         var dirty = false;
46693         this.items.each(function(f){
46694            if(f.hasChanged()){
46695                dirty = true;
46696                return false;
46697            }
46698         });
46699         return dirty;
46700         
46701     },
46702     /**
46703      * Resets all hasChanged to 'false' -
46704      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46705      * So hasChanged storage is only to be used for this purpose
46706      * @return Boolean
46707      */
46708     resetHasChanged : function()
46709     {
46710         this.items.each(function(f){
46711            f.resetHasChanged();
46712         });
46713         
46714     },
46715     
46716     
46717     /**
46718      * Performs a predefined action (submit or load) or custom actions you define on this form.
46719      * @param {String} actionName The name of the action type
46720      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46721      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46722      * accept other config options):
46723      * <pre>
46724 Property          Type             Description
46725 ----------------  ---------------  ----------------------------------------------------------------------------------
46726 url               String           The url for the action (defaults to the form's url)
46727 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46728 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46729 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46730                                    validate the form on the client (defaults to false)
46731      * </pre>
46732      * @return {BasicForm} this
46733      */
46734     doAction : function(action, options){
46735         if(typeof action == 'string'){
46736             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46737         }
46738         if(this.fireEvent('beforeaction', this, action) !== false){
46739             this.beforeAction(action);
46740             action.run.defer(100, action);
46741         }
46742         return this;
46743     },
46744
46745     /**
46746      * Shortcut to do a submit action.
46747      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46748      * @return {BasicForm} this
46749      */
46750     submit : function(options){
46751         this.doAction('submit', options);
46752         return this;
46753     },
46754
46755     /**
46756      * Shortcut to do a load action.
46757      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46758      * @return {BasicForm} this
46759      */
46760     load : function(options){
46761         this.doAction('load', options);
46762         return this;
46763     },
46764
46765     /**
46766      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46767      * @param {Record} record The record to edit
46768      * @return {BasicForm} this
46769      */
46770     updateRecord : function(record){
46771         record.beginEdit();
46772         var fs = record.fields;
46773         fs.each(function(f){
46774             var field = this.findField(f.name);
46775             if(field){
46776                 record.set(f.name, field.getValue());
46777             }
46778         }, this);
46779         record.endEdit();
46780         return this;
46781     },
46782
46783     /**
46784      * Loads an Roo.data.Record into this form.
46785      * @param {Record} record The record to load
46786      * @return {BasicForm} this
46787      */
46788     loadRecord : function(record){
46789         this.setValues(record.data);
46790         return this;
46791     },
46792
46793     // private
46794     beforeAction : function(action){
46795         var o = action.options;
46796         
46797        
46798         if(this.waitMsgTarget === true){
46799             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46800         }else if(this.waitMsgTarget){
46801             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46802             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46803         }else {
46804             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46805         }
46806          
46807     },
46808
46809     // private
46810     afterAction : function(action, success){
46811         this.activeAction = null;
46812         var o = action.options;
46813         
46814         if(this.waitMsgTarget === true){
46815             this.el.unmask();
46816         }else if(this.waitMsgTarget){
46817             this.waitMsgTarget.unmask();
46818         }else{
46819             Roo.MessageBox.updateProgress(1);
46820             Roo.MessageBox.hide();
46821         }
46822          
46823         if(success){
46824             if(o.reset){
46825                 this.reset();
46826             }
46827             Roo.callback(o.success, o.scope, [this, action]);
46828             this.fireEvent('actioncomplete', this, action);
46829             
46830         }else{
46831             
46832             // failure condition..
46833             // we have a scenario where updates need confirming.
46834             // eg. if a locking scenario exists..
46835             // we look for { errors : { needs_confirm : true }} in the response.
46836             if (
46837                 (typeof(action.result) != 'undefined')  &&
46838                 (typeof(action.result.errors) != 'undefined')  &&
46839                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46840            ){
46841                 var _t = this;
46842                 Roo.MessageBox.confirm(
46843                     "Change requires confirmation",
46844                     action.result.errorMsg,
46845                     function(r) {
46846                         if (r != 'yes') {
46847                             return;
46848                         }
46849                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46850                     }
46851                     
46852                 );
46853                 
46854                 
46855                 
46856                 return;
46857             }
46858             
46859             Roo.callback(o.failure, o.scope, [this, action]);
46860             // show an error message if no failed handler is set..
46861             if (!this.hasListener('actionfailed')) {
46862                 Roo.MessageBox.alert("Error",
46863                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46864                         action.result.errorMsg :
46865                         "Saving Failed, please check your entries or try again"
46866                 );
46867             }
46868             
46869             this.fireEvent('actionfailed', this, action);
46870         }
46871         
46872     },
46873
46874     /**
46875      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46876      * @param {String} id The value to search for
46877      * @return Field
46878      */
46879     findField : function(id){
46880         var field = this.items.get(id);
46881         if(!field){
46882             this.items.each(function(f){
46883                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46884                     field = f;
46885                     return false;
46886                 }
46887             });
46888         }
46889         return field || null;
46890     },
46891
46892     /**
46893      * Add a secondary form to this one, 
46894      * Used to provide tabbed forms. One form is primary, with hidden values 
46895      * which mirror the elements from the other forms.
46896      * 
46897      * @param {Roo.form.Form} form to add.
46898      * 
46899      */
46900     addForm : function(form)
46901     {
46902        
46903         if (this.childForms.indexOf(form) > -1) {
46904             // already added..
46905             return;
46906         }
46907         this.childForms.push(form);
46908         var n = '';
46909         Roo.each(form.allItems, function (fe) {
46910             
46911             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46912             if (this.findField(n)) { // already added..
46913                 return;
46914             }
46915             var add = new Roo.form.Hidden({
46916                 name : n
46917             });
46918             add.render(this.el);
46919             
46920             this.add( add );
46921         }, this);
46922         
46923     },
46924     /**
46925      * Mark fields in this form invalid in bulk.
46926      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46927      * @return {BasicForm} this
46928      */
46929     markInvalid : function(errors){
46930         if(errors instanceof Array){
46931             for(var i = 0, len = errors.length; i < len; i++){
46932                 var fieldError = errors[i];
46933                 var f = this.findField(fieldError.id);
46934                 if(f){
46935                     f.markInvalid(fieldError.msg);
46936                 }
46937             }
46938         }else{
46939             var field, id;
46940             for(id in errors){
46941                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46942                     field.markInvalid(errors[id]);
46943                 }
46944             }
46945         }
46946         Roo.each(this.childForms || [], function (f) {
46947             f.markInvalid(errors);
46948         });
46949         
46950         return this;
46951     },
46952
46953     /**
46954      * Set values for fields in this form in bulk.
46955      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46956      * @return {BasicForm} this
46957      */
46958     setValues : function(values){
46959         if(values instanceof Array){ // array of objects
46960             for(var i = 0, len = values.length; i < len; i++){
46961                 var v = values[i];
46962                 var f = this.findField(v.id);
46963                 if(f){
46964                     f.setValue(v.value);
46965                     if(this.trackResetOnLoad){
46966                         f.originalValue = f.getValue();
46967                     }
46968                 }
46969             }
46970         }else{ // object hash
46971             var field, id;
46972             for(id in values){
46973                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46974                     
46975                     if (field.setFromData && 
46976                         field.valueField && 
46977                         field.displayField &&
46978                         // combos' with local stores can 
46979                         // be queried via setValue()
46980                         // to set their value..
46981                         (field.store && !field.store.isLocal)
46982                         ) {
46983                         // it's a combo
46984                         var sd = { };
46985                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46986                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46987                         field.setFromData(sd);
46988                         
46989                     } else {
46990                         field.setValue(values[id]);
46991                     }
46992                     
46993                     
46994                     if(this.trackResetOnLoad){
46995                         field.originalValue = field.getValue();
46996                     }
46997                 }
46998             }
46999         }
47000         this.resetHasChanged();
47001         
47002         
47003         Roo.each(this.childForms || [], function (f) {
47004             f.setValues(values);
47005             f.resetHasChanged();
47006         });
47007                 
47008         return this;
47009     },
47010
47011     /**
47012      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47013      * they are returned as an array.
47014      * @param {Boolean} asString
47015      * @return {Object}
47016      */
47017     getValues : function(asString){
47018         if (this.childForms) {
47019             // copy values from the child forms
47020             Roo.each(this.childForms, function (f) {
47021                 this.setValues(f.getValues());
47022             }, this);
47023         }
47024         
47025         
47026         
47027         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47028         if(asString === true){
47029             return fs;
47030         }
47031         return Roo.urlDecode(fs);
47032     },
47033     
47034     /**
47035      * Returns the fields in this form as an object with key/value pairs. 
47036      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47037      * @return {Object}
47038      */
47039     getFieldValues : function(with_hidden)
47040     {
47041         if (this.childForms) {
47042             // copy values from the child forms
47043             // should this call getFieldValues - probably not as we do not currently copy
47044             // hidden fields when we generate..
47045             Roo.each(this.childForms, function (f) {
47046                 this.setValues(f.getValues());
47047             }, this);
47048         }
47049         
47050         var ret = {};
47051         this.items.each(function(f){
47052             if (!f.getName()) {
47053                 return;
47054             }
47055             var v = f.getValue();
47056             if (f.inputType =='radio') {
47057                 if (typeof(ret[f.getName()]) == 'undefined') {
47058                     ret[f.getName()] = ''; // empty..
47059                 }
47060                 
47061                 if (!f.el.dom.checked) {
47062                     return;
47063                     
47064                 }
47065                 v = f.el.dom.value;
47066                 
47067             }
47068             
47069             // not sure if this supported any more..
47070             if ((typeof(v) == 'object') && f.getRawValue) {
47071                 v = f.getRawValue() ; // dates..
47072             }
47073             // combo boxes where name != hiddenName...
47074             if (f.name != f.getName()) {
47075                 ret[f.name] = f.getRawValue();
47076             }
47077             ret[f.getName()] = v;
47078         });
47079         
47080         return ret;
47081     },
47082
47083     /**
47084      * Clears all invalid messages in this form.
47085      * @return {BasicForm} this
47086      */
47087     clearInvalid : function(){
47088         this.items.each(function(f){
47089            f.clearInvalid();
47090         });
47091         
47092         Roo.each(this.childForms || [], function (f) {
47093             f.clearInvalid();
47094         });
47095         
47096         
47097         return this;
47098     },
47099
47100     /**
47101      * Resets this form.
47102      * @return {BasicForm} this
47103      */
47104     reset : function(){
47105         this.items.each(function(f){
47106             f.reset();
47107         });
47108         
47109         Roo.each(this.childForms || [], function (f) {
47110             f.reset();
47111         });
47112         this.resetHasChanged();
47113         
47114         return this;
47115     },
47116
47117     /**
47118      * Add Roo.form components to this form.
47119      * @param {Field} field1
47120      * @param {Field} field2 (optional)
47121      * @param {Field} etc (optional)
47122      * @return {BasicForm} this
47123      */
47124     add : function(){
47125         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47126         return this;
47127     },
47128
47129
47130     /**
47131      * Removes a field from the items collection (does NOT remove its markup).
47132      * @param {Field} field
47133      * @return {BasicForm} this
47134      */
47135     remove : function(field){
47136         this.items.remove(field);
47137         return this;
47138     },
47139
47140     /**
47141      * Looks at the fields in this form, checks them for an id attribute,
47142      * and calls applyTo on the existing dom element with that id.
47143      * @return {BasicForm} this
47144      */
47145     render : function(){
47146         this.items.each(function(f){
47147             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47148                 f.applyTo(f.id);
47149             }
47150         });
47151         return this;
47152     },
47153
47154     /**
47155      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47156      * @param {Object} values
47157      * @return {BasicForm} this
47158      */
47159     applyToFields : function(o){
47160         this.items.each(function(f){
47161            Roo.apply(f, o);
47162         });
47163         return this;
47164     },
47165
47166     /**
47167      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47168      * @param {Object} values
47169      * @return {BasicForm} this
47170      */
47171     applyIfToFields : function(o){
47172         this.items.each(function(f){
47173            Roo.applyIf(f, o);
47174         });
47175         return this;
47176     }
47177 });
47178
47179 // back compat
47180 Roo.BasicForm = Roo.form.BasicForm;/*
47181  * Based on:
47182  * Ext JS Library 1.1.1
47183  * Copyright(c) 2006-2007, Ext JS, LLC.
47184  *
47185  * Originally Released Under LGPL - original licence link has changed is not relivant.
47186  *
47187  * Fork - LGPL
47188  * <script type="text/javascript">
47189  */
47190
47191 /**
47192  * @class Roo.form.Form
47193  * @extends Roo.form.BasicForm
47194  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47195  * @constructor
47196  * @param {Object} config Configuration options
47197  */
47198 Roo.form.Form = function(config){
47199     var xitems =  [];
47200     if (config.items) {
47201         xitems = config.items;
47202         delete config.items;
47203     }
47204    
47205     
47206     Roo.form.Form.superclass.constructor.call(this, null, config);
47207     this.url = this.url || this.action;
47208     if(!this.root){
47209         this.root = new Roo.form.Layout(Roo.applyIf({
47210             id: Roo.id()
47211         }, config));
47212     }
47213     this.active = this.root;
47214     /**
47215      * Array of all the buttons that have been added to this form via {@link addButton}
47216      * @type Array
47217      */
47218     this.buttons = [];
47219     this.allItems = [];
47220     this.addEvents({
47221         /**
47222          * @event clientvalidation
47223          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47224          * @param {Form} this
47225          * @param {Boolean} valid true if the form has passed client-side validation
47226          */
47227         clientvalidation: true,
47228         /**
47229          * @event rendered
47230          * Fires when the form is rendered
47231          * @param {Roo.form.Form} form
47232          */
47233         rendered : true
47234     });
47235     
47236     if (this.progressUrl) {
47237             // push a hidden field onto the list of fields..
47238             this.addxtype( {
47239                     xns: Roo.form, 
47240                     xtype : 'Hidden', 
47241                     name : 'UPLOAD_IDENTIFIER' 
47242             });
47243         }
47244         
47245     
47246     Roo.each(xitems, this.addxtype, this);
47247     
47248     
47249     
47250 };
47251
47252 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47253     /**
47254      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47255      */
47256     /**
47257      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47258      */
47259     /**
47260      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47261      */
47262     buttonAlign:'center',
47263
47264     /**
47265      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47266      */
47267     minButtonWidth:75,
47268
47269     /**
47270      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47271      * This property cascades to child containers if not set.
47272      */
47273     labelAlign:'left',
47274
47275     /**
47276      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47277      * fires a looping event with that state. This is required to bind buttons to the valid
47278      * state using the config value formBind:true on the button.
47279      */
47280     monitorValid : false,
47281
47282     /**
47283      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47284      */
47285     monitorPoll : 200,
47286     
47287     /**
47288      * @cfg {String} progressUrl - Url to return progress data 
47289      */
47290     
47291     progressUrl : false,
47292   
47293     /**
47294      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47295      * fields are added and the column is closed. If no fields are passed the column remains open
47296      * until end() is called.
47297      * @param {Object} config The config to pass to the column
47298      * @param {Field} field1 (optional)
47299      * @param {Field} field2 (optional)
47300      * @param {Field} etc (optional)
47301      * @return Column The column container object
47302      */
47303     column : function(c){
47304         var col = new Roo.form.Column(c);
47305         this.start(col);
47306         if(arguments.length > 1){ // duplicate code required because of Opera
47307             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47308             this.end();
47309         }
47310         return col;
47311     },
47312
47313     /**
47314      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47315      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47316      * until end() is called.
47317      * @param {Object} config The config to pass to the fieldset
47318      * @param {Field} field1 (optional)
47319      * @param {Field} field2 (optional)
47320      * @param {Field} etc (optional)
47321      * @return FieldSet The fieldset container object
47322      */
47323     fieldset : function(c){
47324         var fs = new Roo.form.FieldSet(c);
47325         this.start(fs);
47326         if(arguments.length > 1){ // duplicate code required because of Opera
47327             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47328             this.end();
47329         }
47330         return fs;
47331     },
47332
47333     /**
47334      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47335      * fields are added and the container is closed. If no fields are passed the container remains open
47336      * until end() is called.
47337      * @param {Object} config The config to pass to the Layout
47338      * @param {Field} field1 (optional)
47339      * @param {Field} field2 (optional)
47340      * @param {Field} etc (optional)
47341      * @return Layout The container object
47342      */
47343     container : function(c){
47344         var l = new Roo.form.Layout(c);
47345         this.start(l);
47346         if(arguments.length > 1){ // duplicate code required because of Opera
47347             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47348             this.end();
47349         }
47350         return l;
47351     },
47352
47353     /**
47354      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47355      * @param {Object} container A Roo.form.Layout or subclass of Layout
47356      * @return {Form} this
47357      */
47358     start : function(c){
47359         // cascade label info
47360         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47361         this.active.stack.push(c);
47362         c.ownerCt = this.active;
47363         this.active = c;
47364         return this;
47365     },
47366
47367     /**
47368      * Closes the current open container
47369      * @return {Form} this
47370      */
47371     end : function(){
47372         if(this.active == this.root){
47373             return this;
47374         }
47375         this.active = this.active.ownerCt;
47376         return this;
47377     },
47378
47379     /**
47380      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47381      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47382      * as the label of the field.
47383      * @param {Field} field1
47384      * @param {Field} field2 (optional)
47385      * @param {Field} etc. (optional)
47386      * @return {Form} this
47387      */
47388     add : function(){
47389         this.active.stack.push.apply(this.active.stack, arguments);
47390         this.allItems.push.apply(this.allItems,arguments);
47391         var r = [];
47392         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47393             if(a[i].isFormField){
47394                 r.push(a[i]);
47395             }
47396         }
47397         if(r.length > 0){
47398             Roo.form.Form.superclass.add.apply(this, r);
47399         }
47400         return this;
47401     },
47402     
47403
47404     
47405     
47406     
47407      /**
47408      * Find any element that has been added to a form, using it's ID or name
47409      * This can include framesets, columns etc. along with regular fields..
47410      * @param {String} id - id or name to find.
47411      
47412      * @return {Element} e - or false if nothing found.
47413      */
47414     findbyId : function(id)
47415     {
47416         var ret = false;
47417         if (!id) {
47418             return ret;
47419         }
47420         Roo.each(this.allItems, function(f){
47421             if (f.id == id || f.name == id ){
47422                 ret = f;
47423                 return false;
47424             }
47425         });
47426         return ret;
47427     },
47428
47429     
47430     
47431     /**
47432      * Render this form into the passed container. This should only be called once!
47433      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47434      * @return {Form} this
47435      */
47436     render : function(ct)
47437     {
47438         
47439         
47440         
47441         ct = Roo.get(ct);
47442         var o = this.autoCreate || {
47443             tag: 'form',
47444             method : this.method || 'POST',
47445             id : this.id || Roo.id()
47446         };
47447         this.initEl(ct.createChild(o));
47448
47449         this.root.render(this.el);
47450         
47451        
47452              
47453         this.items.each(function(f){
47454             f.render('x-form-el-'+f.id);
47455         });
47456
47457         if(this.buttons.length > 0){
47458             // tables are required to maintain order and for correct IE layout
47459             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47460                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47462             }}, null, true);
47463             var tr = tb.getElementsByTagName('tr')[0];
47464             for(var i = 0, len = this.buttons.length; i < len; i++) {
47465                 var b = this.buttons[i];
47466                 var td = document.createElement('td');
47467                 td.className = 'x-form-btn-td';
47468                 b.render(tr.appendChild(td));
47469             }
47470         }
47471         if(this.monitorValid){ // initialize after render
47472             this.startMonitoring();
47473         }
47474         this.fireEvent('rendered', this);
47475         return this;
47476     },
47477
47478     /**
47479      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47480      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47481      * object or a valid Roo.DomHelper element config
47482      * @param {Function} handler The function called when the button is clicked
47483      * @param {Object} scope (optional) The scope of the handler function
47484      * @return {Roo.Button}
47485      */
47486     addButton : function(config, handler, scope){
47487         var bc = {
47488             handler: handler,
47489             scope: scope,
47490             minWidth: this.minButtonWidth,
47491             hideParent:true
47492         };
47493         if(typeof config == "string"){
47494             bc.text = config;
47495         }else{
47496             Roo.apply(bc, config);
47497         }
47498         var btn = new Roo.Button(null, bc);
47499         this.buttons.push(btn);
47500         return btn;
47501     },
47502
47503      /**
47504      * Adds a series of form elements (using the xtype property as the factory method.
47505      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47506      * @param {Object} config 
47507      */
47508     
47509     addxtype : function()
47510     {
47511         var ar = Array.prototype.slice.call(arguments, 0);
47512         var ret = false;
47513         for(var i = 0; i < ar.length; i++) {
47514             if (!ar[i]) {
47515                 continue; // skip -- if this happends something invalid got sent, we 
47516                 // should ignore it, as basically that interface element will not show up
47517                 // and that should be pretty obvious!!
47518             }
47519             
47520             if (Roo.form[ar[i].xtype]) {
47521                 ar[i].form = this;
47522                 var fe = Roo.factory(ar[i], Roo.form);
47523                 if (!ret) {
47524                     ret = fe;
47525                 }
47526                 fe.form = this;
47527                 if (fe.store) {
47528                     fe.store.form = this;
47529                 }
47530                 if (fe.isLayout) {  
47531                          
47532                     this.start(fe);
47533                     this.allItems.push(fe);
47534                     if (fe.items && fe.addxtype) {
47535                         fe.addxtype.apply(fe, fe.items);
47536                         delete fe.items;
47537                     }
47538                      this.end();
47539                     continue;
47540                 }
47541                 
47542                 
47543                  
47544                 this.add(fe);
47545               //  console.log('adding ' + ar[i].xtype);
47546             }
47547             if (ar[i].xtype == 'Button') {  
47548                 //console.log('adding button');
47549                 //console.log(ar[i]);
47550                 this.addButton(ar[i]);
47551                 this.allItems.push(fe);
47552                 continue;
47553             }
47554             
47555             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47556                 alert('end is not supported on xtype any more, use items');
47557             //    this.end();
47558             //    //console.log('adding end');
47559             }
47560             
47561         }
47562         return ret;
47563     },
47564     
47565     /**
47566      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47567      * option "monitorValid"
47568      */
47569     startMonitoring : function(){
47570         if(!this.bound){
47571             this.bound = true;
47572             Roo.TaskMgr.start({
47573                 run : this.bindHandler,
47574                 interval : this.monitorPoll || 200,
47575                 scope: this
47576             });
47577         }
47578     },
47579
47580     /**
47581      * Stops monitoring of the valid state of this form
47582      */
47583     stopMonitoring : function(){
47584         this.bound = false;
47585     },
47586
47587     // private
47588     bindHandler : function(){
47589         if(!this.bound){
47590             return false; // stops binding
47591         }
47592         var valid = true;
47593         this.items.each(function(f){
47594             if(!f.isValid(true)){
47595                 valid = false;
47596                 return false;
47597             }
47598         });
47599         for(var i = 0, len = this.buttons.length; i < len; i++){
47600             var btn = this.buttons[i];
47601             if(btn.formBind === true && btn.disabled === valid){
47602                 btn.setDisabled(!valid);
47603             }
47604         }
47605         this.fireEvent('clientvalidation', this, valid);
47606     }
47607     
47608     
47609     
47610     
47611     
47612     
47613     
47614     
47615 });
47616
47617
47618 // back compat
47619 Roo.Form = Roo.form.Form;
47620 /*
47621  * Based on:
47622  * Ext JS Library 1.1.1
47623  * Copyright(c) 2006-2007, Ext JS, LLC.
47624  *
47625  * Originally Released Under LGPL - original licence link has changed is not relivant.
47626  *
47627  * Fork - LGPL
47628  * <script type="text/javascript">
47629  */
47630
47631 // as we use this in bootstrap.
47632 Roo.namespace('Roo.form');
47633  /**
47634  * @class Roo.form.Action
47635  * Internal Class used to handle form actions
47636  * @constructor
47637  * @param {Roo.form.BasicForm} el The form element or its id
47638  * @param {Object} config Configuration options
47639  */
47640
47641  
47642  
47643 // define the action interface
47644 Roo.form.Action = function(form, options){
47645     this.form = form;
47646     this.options = options || {};
47647 };
47648 /**
47649  * Client Validation Failed
47650  * @const 
47651  */
47652 Roo.form.Action.CLIENT_INVALID = 'client';
47653 /**
47654  * Server Validation Failed
47655  * @const 
47656  */
47657 Roo.form.Action.SERVER_INVALID = 'server';
47658  /**
47659  * Connect to Server Failed
47660  * @const 
47661  */
47662 Roo.form.Action.CONNECT_FAILURE = 'connect';
47663 /**
47664  * Reading Data from Server Failed
47665  * @const 
47666  */
47667 Roo.form.Action.LOAD_FAILURE = 'load';
47668
47669 Roo.form.Action.prototype = {
47670     type : 'default',
47671     failureType : undefined,
47672     response : undefined,
47673     result : undefined,
47674
47675     // interface method
47676     run : function(options){
47677
47678     },
47679
47680     // interface method
47681     success : function(response){
47682
47683     },
47684
47685     // interface method
47686     handleResponse : function(response){
47687
47688     },
47689
47690     // default connection failure
47691     failure : function(response){
47692         
47693         this.response = response;
47694         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47695         this.form.afterAction(this, false);
47696     },
47697
47698     processResponse : function(response){
47699         this.response = response;
47700         if(!response.responseText){
47701             return true;
47702         }
47703         this.result = this.handleResponse(response);
47704         return this.result;
47705     },
47706
47707     // utility functions used internally
47708     getUrl : function(appendParams){
47709         var url = this.options.url || this.form.url || this.form.el.dom.action;
47710         if(appendParams){
47711             var p = this.getParams();
47712             if(p){
47713                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47714             }
47715         }
47716         return url;
47717     },
47718
47719     getMethod : function(){
47720         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47721     },
47722
47723     getParams : function(){
47724         var bp = this.form.baseParams;
47725         var p = this.options.params;
47726         if(p){
47727             if(typeof p == "object"){
47728                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47729             }else if(typeof p == 'string' && bp){
47730                 p += '&' + Roo.urlEncode(bp);
47731             }
47732         }else if(bp){
47733             p = Roo.urlEncode(bp);
47734         }
47735         return p;
47736     },
47737
47738     createCallback : function(){
47739         return {
47740             success: this.success,
47741             failure: this.failure,
47742             scope: this,
47743             timeout: (this.form.timeout*1000),
47744             upload: this.form.fileUpload ? this.success : undefined
47745         };
47746     }
47747 };
47748
47749 Roo.form.Action.Submit = function(form, options){
47750     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47751 };
47752
47753 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47754     type : 'submit',
47755
47756     haveProgress : false,
47757     uploadComplete : false,
47758     
47759     // uploadProgress indicator.
47760     uploadProgress : function()
47761     {
47762         if (!this.form.progressUrl) {
47763             return;
47764         }
47765         
47766         if (!this.haveProgress) {
47767             Roo.MessageBox.progress("Uploading", "Uploading");
47768         }
47769         if (this.uploadComplete) {
47770            Roo.MessageBox.hide();
47771            return;
47772         }
47773         
47774         this.haveProgress = true;
47775    
47776         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47777         
47778         var c = new Roo.data.Connection();
47779         c.request({
47780             url : this.form.progressUrl,
47781             params: {
47782                 id : uid
47783             },
47784             method: 'GET',
47785             success : function(req){
47786                //console.log(data);
47787                 var rdata = false;
47788                 var edata;
47789                 try  {
47790                    rdata = Roo.decode(req.responseText)
47791                 } catch (e) {
47792                     Roo.log("Invalid data from server..");
47793                     Roo.log(edata);
47794                     return;
47795                 }
47796                 if (!rdata || !rdata.success) {
47797                     Roo.log(rdata);
47798                     Roo.MessageBox.alert(Roo.encode(rdata));
47799                     return;
47800                 }
47801                 var data = rdata.data;
47802                 
47803                 if (this.uploadComplete) {
47804                    Roo.MessageBox.hide();
47805                    return;
47806                 }
47807                    
47808                 if (data){
47809                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47810                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47811                     );
47812                 }
47813                 this.uploadProgress.defer(2000,this);
47814             },
47815        
47816             failure: function(data) {
47817                 Roo.log('progress url failed ');
47818                 Roo.log(data);
47819             },
47820             scope : this
47821         });
47822            
47823     },
47824     
47825     
47826     run : function()
47827     {
47828         // run get Values on the form, so it syncs any secondary forms.
47829         this.form.getValues();
47830         
47831         var o = this.options;
47832         var method = this.getMethod();
47833         var isPost = method == 'POST';
47834         if(o.clientValidation === false || this.form.isValid()){
47835             
47836             if (this.form.progressUrl) {
47837                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47838                     (new Date() * 1) + '' + Math.random());
47839                     
47840             } 
47841             
47842             
47843             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47844                 form:this.form.el.dom,
47845                 url:this.getUrl(!isPost),
47846                 method: method,
47847                 params:isPost ? this.getParams() : null,
47848                 isUpload: this.form.fileUpload
47849             }));
47850             
47851             this.uploadProgress();
47852
47853         }else if (o.clientValidation !== false){ // client validation failed
47854             this.failureType = Roo.form.Action.CLIENT_INVALID;
47855             this.form.afterAction(this, false);
47856         }
47857     },
47858
47859     success : function(response)
47860     {
47861         this.uploadComplete= true;
47862         if (this.haveProgress) {
47863             Roo.MessageBox.hide();
47864         }
47865         
47866         
47867         var result = this.processResponse(response);
47868         if(result === true || result.success){
47869             this.form.afterAction(this, true);
47870             return;
47871         }
47872         if(result.errors){
47873             this.form.markInvalid(result.errors);
47874             this.failureType = Roo.form.Action.SERVER_INVALID;
47875         }
47876         this.form.afterAction(this, false);
47877     },
47878     failure : function(response)
47879     {
47880         this.uploadComplete= true;
47881         if (this.haveProgress) {
47882             Roo.MessageBox.hide();
47883         }
47884         
47885         this.response = response;
47886         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47887         this.form.afterAction(this, false);
47888     },
47889     
47890     handleResponse : function(response){
47891         if(this.form.errorReader){
47892             var rs = this.form.errorReader.read(response);
47893             var errors = [];
47894             if(rs.records){
47895                 for(var i = 0, len = rs.records.length; i < len; i++) {
47896                     var r = rs.records[i];
47897                     errors[i] = r.data;
47898                 }
47899             }
47900             if(errors.length < 1){
47901                 errors = null;
47902             }
47903             return {
47904                 success : rs.success,
47905                 errors : errors
47906             };
47907         }
47908         var ret = false;
47909         try {
47910             ret = Roo.decode(response.responseText);
47911         } catch (e) {
47912             ret = {
47913                 success: false,
47914                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47915                 errors : []
47916             };
47917         }
47918         return ret;
47919         
47920     }
47921 });
47922
47923
47924 Roo.form.Action.Load = function(form, options){
47925     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47926     this.reader = this.form.reader;
47927 };
47928
47929 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47930     type : 'load',
47931
47932     run : function(){
47933         
47934         Roo.Ajax.request(Roo.apply(
47935                 this.createCallback(), {
47936                     method:this.getMethod(),
47937                     url:this.getUrl(false),
47938                     params:this.getParams()
47939         }));
47940     },
47941
47942     success : function(response){
47943         
47944         var result = this.processResponse(response);
47945         if(result === true || !result.success || !result.data){
47946             this.failureType = Roo.form.Action.LOAD_FAILURE;
47947             this.form.afterAction(this, false);
47948             return;
47949         }
47950         this.form.clearInvalid();
47951         this.form.setValues(result.data);
47952         this.form.afterAction(this, true);
47953     },
47954
47955     handleResponse : function(response){
47956         if(this.form.reader){
47957             var rs = this.form.reader.read(response);
47958             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47959             return {
47960                 success : rs.success,
47961                 data : data
47962             };
47963         }
47964         return Roo.decode(response.responseText);
47965     }
47966 });
47967
47968 Roo.form.Action.ACTION_TYPES = {
47969     'load' : Roo.form.Action.Load,
47970     'submit' : Roo.form.Action.Submit
47971 };/*
47972  * Based on:
47973  * Ext JS Library 1.1.1
47974  * Copyright(c) 2006-2007, Ext JS, LLC.
47975  *
47976  * Originally Released Under LGPL - original licence link has changed is not relivant.
47977  *
47978  * Fork - LGPL
47979  * <script type="text/javascript">
47980  */
47981  
47982 /**
47983  * @class Roo.form.Layout
47984  * @extends Roo.Component
47985  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47986  * @constructor
47987  * @param {Object} config Configuration options
47988  */
47989 Roo.form.Layout = function(config){
47990     var xitems = [];
47991     if (config.items) {
47992         xitems = config.items;
47993         delete config.items;
47994     }
47995     Roo.form.Layout.superclass.constructor.call(this, config);
47996     this.stack = [];
47997     Roo.each(xitems, this.addxtype, this);
47998      
47999 };
48000
48001 Roo.extend(Roo.form.Layout, Roo.Component, {
48002     /**
48003      * @cfg {String/Object} autoCreate
48004      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48005      */
48006     /**
48007      * @cfg {String/Object/Function} style
48008      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48009      * a function which returns such a specification.
48010      */
48011     /**
48012      * @cfg {String} labelAlign
48013      * Valid values are "left," "top" and "right" (defaults to "left")
48014      */
48015     /**
48016      * @cfg {Number} labelWidth
48017      * Fixed width in pixels of all field labels (defaults to undefined)
48018      */
48019     /**
48020      * @cfg {Boolean} clear
48021      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48022      */
48023     clear : true,
48024     /**
48025      * @cfg {String} labelSeparator
48026      * The separator to use after field labels (defaults to ':')
48027      */
48028     labelSeparator : ':',
48029     /**
48030      * @cfg {Boolean} hideLabels
48031      * True to suppress the display of field labels in this layout (defaults to false)
48032      */
48033     hideLabels : false,
48034
48035     // private
48036     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48037     
48038     isLayout : true,
48039     
48040     // private
48041     onRender : function(ct, position){
48042         if(this.el){ // from markup
48043             this.el = Roo.get(this.el);
48044         }else {  // generate
48045             var cfg = this.getAutoCreate();
48046             this.el = ct.createChild(cfg, position);
48047         }
48048         if(this.style){
48049             this.el.applyStyles(this.style);
48050         }
48051         if(this.labelAlign){
48052             this.el.addClass('x-form-label-'+this.labelAlign);
48053         }
48054         if(this.hideLabels){
48055             this.labelStyle = "display:none";
48056             this.elementStyle = "padding-left:0;";
48057         }else{
48058             if(typeof this.labelWidth == 'number'){
48059                 this.labelStyle = "width:"+this.labelWidth+"px;";
48060                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48061             }
48062             if(this.labelAlign == 'top'){
48063                 this.labelStyle = "width:auto;";
48064                 this.elementStyle = "padding-left:0;";
48065             }
48066         }
48067         var stack = this.stack;
48068         var slen = stack.length;
48069         if(slen > 0){
48070             if(!this.fieldTpl){
48071                 var t = new Roo.Template(
48072                     '<div class="x-form-item {5}">',
48073                         '<label for="{0}" style="{2}">{1}{4}</label>',
48074                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48075                         '</div>',
48076                     '</div><div class="x-form-clear-left"></div>'
48077                 );
48078                 t.disableFormats = true;
48079                 t.compile();
48080                 Roo.form.Layout.prototype.fieldTpl = t;
48081             }
48082             for(var i = 0; i < slen; i++) {
48083                 if(stack[i].isFormField){
48084                     this.renderField(stack[i]);
48085                 }else{
48086                     this.renderComponent(stack[i]);
48087                 }
48088             }
48089         }
48090         if(this.clear){
48091             this.el.createChild({cls:'x-form-clear'});
48092         }
48093     },
48094
48095     // private
48096     renderField : function(f){
48097         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48098                f.id, //0
48099                f.fieldLabel, //1
48100                f.labelStyle||this.labelStyle||'', //2
48101                this.elementStyle||'', //3
48102                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48103                f.itemCls||this.itemCls||''  //5
48104        ], true).getPrevSibling());
48105     },
48106
48107     // private
48108     renderComponent : function(c){
48109         c.render(c.isLayout ? this.el : this.el.createChild());    
48110     },
48111     /**
48112      * Adds a object form elements (using the xtype property as the factory method.)
48113      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48114      * @param {Object} config 
48115      */
48116     addxtype : function(o)
48117     {
48118         // create the lement.
48119         o.form = this.form;
48120         var fe = Roo.factory(o, Roo.form);
48121         this.form.allItems.push(fe);
48122         this.stack.push(fe);
48123         
48124         if (fe.isFormField) {
48125             this.form.items.add(fe);
48126         }
48127          
48128         return fe;
48129     }
48130 });
48131
48132 /**
48133  * @class Roo.form.Column
48134  * @extends Roo.form.Layout
48135  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48136  * @constructor
48137  * @param {Object} config Configuration options
48138  */
48139 Roo.form.Column = function(config){
48140     Roo.form.Column.superclass.constructor.call(this, config);
48141 };
48142
48143 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48144     /**
48145      * @cfg {Number/String} width
48146      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48147      */
48148     /**
48149      * @cfg {String/Object} autoCreate
48150      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48151      */
48152
48153     // private
48154     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48155
48156     // private
48157     onRender : function(ct, position){
48158         Roo.form.Column.superclass.onRender.call(this, ct, position);
48159         if(this.width){
48160             this.el.setWidth(this.width);
48161         }
48162     }
48163 });
48164
48165
48166 /**
48167  * @class Roo.form.Row
48168  * @extends Roo.form.Layout
48169  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48170  * @constructor
48171  * @param {Object} config Configuration options
48172  */
48173
48174  
48175 Roo.form.Row = function(config){
48176     Roo.form.Row.superclass.constructor.call(this, config);
48177 };
48178  
48179 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48180       /**
48181      * @cfg {Number/String} width
48182      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48183      */
48184     /**
48185      * @cfg {Number/String} height
48186      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48187      */
48188     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48189     
48190     padWidth : 20,
48191     // private
48192     onRender : function(ct, position){
48193         //console.log('row render');
48194         if(!this.rowTpl){
48195             var t = new Roo.Template(
48196                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48197                     '<label for="{0}" style="{2}">{1}{4}</label>',
48198                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48199                     '</div>',
48200                 '</div>'
48201             );
48202             t.disableFormats = true;
48203             t.compile();
48204             Roo.form.Layout.prototype.rowTpl = t;
48205         }
48206         this.fieldTpl = this.rowTpl;
48207         
48208         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48209         var labelWidth = 100;
48210         
48211         if ((this.labelAlign != 'top')) {
48212             if (typeof this.labelWidth == 'number') {
48213                 labelWidth = this.labelWidth
48214             }
48215             this.padWidth =  20 + labelWidth;
48216             
48217         }
48218         
48219         Roo.form.Column.superclass.onRender.call(this, ct, position);
48220         if(this.width){
48221             this.el.setWidth(this.width);
48222         }
48223         if(this.height){
48224             this.el.setHeight(this.height);
48225         }
48226     },
48227     
48228     // private
48229     renderField : function(f){
48230         f.fieldEl = this.fieldTpl.append(this.el, [
48231                f.id, f.fieldLabel,
48232                f.labelStyle||this.labelStyle||'',
48233                this.elementStyle||'',
48234                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48235                f.itemCls||this.itemCls||'',
48236                f.width ? f.width + this.padWidth : 160 + this.padWidth
48237        ],true);
48238     }
48239 });
48240  
48241
48242 /**
48243  * @class Roo.form.FieldSet
48244  * @extends Roo.form.Layout
48245  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48246  * @constructor
48247  * @param {Object} config Configuration options
48248  */
48249 Roo.form.FieldSet = function(config){
48250     Roo.form.FieldSet.superclass.constructor.call(this, config);
48251 };
48252
48253 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48254     /**
48255      * @cfg {String} legend
48256      * The text to display as the legend for the FieldSet (defaults to '')
48257      */
48258     /**
48259      * @cfg {String/Object} autoCreate
48260      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48261      */
48262
48263     // private
48264     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48265
48266     // private
48267     onRender : function(ct, position){
48268         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48269         if(this.legend){
48270             this.setLegend(this.legend);
48271         }
48272     },
48273
48274     // private
48275     setLegend : function(text){
48276         if(this.rendered){
48277             this.el.child('legend').update(text);
48278         }
48279     }
48280 });/*
48281  * Based on:
48282  * Ext JS Library 1.1.1
48283  * Copyright(c) 2006-2007, Ext JS, LLC.
48284  *
48285  * Originally Released Under LGPL - original licence link has changed is not relivant.
48286  *
48287  * Fork - LGPL
48288  * <script type="text/javascript">
48289  */
48290 /**
48291  * @class Roo.form.VTypes
48292  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48293  * @singleton
48294  */
48295 Roo.form.VTypes = function(){
48296     // closure these in so they are only created once.
48297     var alpha = /^[a-zA-Z_]+$/;
48298     var alphanum = /^[a-zA-Z0-9_]+$/;
48299     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48300     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48301
48302     // All these messages and functions are configurable
48303     return {
48304         /**
48305          * The function used to validate email addresses
48306          * @param {String} value The email address
48307          */
48308         'email' : function(v){
48309             return email.test(v);
48310         },
48311         /**
48312          * The error text to display when the email validation function returns false
48313          * @type String
48314          */
48315         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48316         /**
48317          * The keystroke filter mask to be applied on email input
48318          * @type RegExp
48319          */
48320         'emailMask' : /[a-z0-9_\.\-@]/i,
48321
48322         /**
48323          * The function used to validate URLs
48324          * @param {String} value The URL
48325          */
48326         'url' : function(v){
48327             return url.test(v);
48328         },
48329         /**
48330          * The error text to display when the url validation function returns false
48331          * @type String
48332          */
48333         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48334         
48335         /**
48336          * The function used to validate alpha values
48337          * @param {String} value The value
48338          */
48339         'alpha' : function(v){
48340             return alpha.test(v);
48341         },
48342         /**
48343          * The error text to display when the alpha validation function returns false
48344          * @type String
48345          */
48346         'alphaText' : 'This field should only contain letters and _',
48347         /**
48348          * The keystroke filter mask to be applied on alpha input
48349          * @type RegExp
48350          */
48351         'alphaMask' : /[a-z_]/i,
48352
48353         /**
48354          * The function used to validate alphanumeric values
48355          * @param {String} value The value
48356          */
48357         'alphanum' : function(v){
48358             return alphanum.test(v);
48359         },
48360         /**
48361          * The error text to display when the alphanumeric validation function returns false
48362          * @type String
48363          */
48364         'alphanumText' : 'This field should only contain letters, numbers and _',
48365         /**
48366          * The keystroke filter mask to be applied on alphanumeric input
48367          * @type RegExp
48368          */
48369         'alphanumMask' : /[a-z0-9_]/i
48370     };
48371 }();//<script type="text/javascript">
48372
48373 /**
48374  * @class Roo.form.FCKeditor
48375  * @extends Roo.form.TextArea
48376  * Wrapper around the FCKEditor http://www.fckeditor.net
48377  * @constructor
48378  * Creates a new FCKeditor
48379  * @param {Object} config Configuration options
48380  */
48381 Roo.form.FCKeditor = function(config){
48382     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48383     this.addEvents({
48384          /**
48385          * @event editorinit
48386          * Fired when the editor is initialized - you can add extra handlers here..
48387          * @param {FCKeditor} this
48388          * @param {Object} the FCK object.
48389          */
48390         editorinit : true
48391     });
48392     
48393     
48394 };
48395 Roo.form.FCKeditor.editors = { };
48396 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48397 {
48398     //defaultAutoCreate : {
48399     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48400     //},
48401     // private
48402     /**
48403      * @cfg {Object} fck options - see fck manual for details.
48404      */
48405     fckconfig : false,
48406     
48407     /**
48408      * @cfg {Object} fck toolbar set (Basic or Default)
48409      */
48410     toolbarSet : 'Basic',
48411     /**
48412      * @cfg {Object} fck BasePath
48413      */ 
48414     basePath : '/fckeditor/',
48415     
48416     
48417     frame : false,
48418     
48419     value : '',
48420     
48421    
48422     onRender : function(ct, position)
48423     {
48424         if(!this.el){
48425             this.defaultAutoCreate = {
48426                 tag: "textarea",
48427                 style:"width:300px;height:60px;",
48428                 autocomplete: "new-password"
48429             };
48430         }
48431         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48432         /*
48433         if(this.grow){
48434             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48435             if(this.preventScrollbars){
48436                 this.el.setStyle("overflow", "hidden");
48437             }
48438             this.el.setHeight(this.growMin);
48439         }
48440         */
48441         //console.log('onrender' + this.getId() );
48442         Roo.form.FCKeditor.editors[this.getId()] = this;
48443          
48444
48445         this.replaceTextarea() ;
48446         
48447     },
48448     
48449     getEditor : function() {
48450         return this.fckEditor;
48451     },
48452     /**
48453      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48454      * @param {Mixed} value The value to set
48455      */
48456     
48457     
48458     setValue : function(value)
48459     {
48460         //console.log('setValue: ' + value);
48461         
48462         if(typeof(value) == 'undefined') { // not sure why this is happending...
48463             return;
48464         }
48465         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48466         
48467         //if(!this.el || !this.getEditor()) {
48468         //    this.value = value;
48469             //this.setValue.defer(100,this,[value]);    
48470         //    return;
48471         //} 
48472         
48473         if(!this.getEditor()) {
48474             return;
48475         }
48476         
48477         this.getEditor().SetData(value);
48478         
48479         //
48480
48481     },
48482
48483     /**
48484      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48485      * @return {Mixed} value The field value
48486      */
48487     getValue : function()
48488     {
48489         
48490         if (this.frame && this.frame.dom.style.display == 'none') {
48491             return Roo.form.FCKeditor.superclass.getValue.call(this);
48492         }
48493         
48494         if(!this.el || !this.getEditor()) {
48495            
48496            // this.getValue.defer(100,this); 
48497             return this.value;
48498         }
48499        
48500         
48501         var value=this.getEditor().GetData();
48502         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48503         return Roo.form.FCKeditor.superclass.getValue.call(this);
48504         
48505
48506     },
48507
48508     /**
48509      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48510      * @return {Mixed} value The field value
48511      */
48512     getRawValue : function()
48513     {
48514         if (this.frame && this.frame.dom.style.display == 'none') {
48515             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48516         }
48517         
48518         if(!this.el || !this.getEditor()) {
48519             //this.getRawValue.defer(100,this); 
48520             return this.value;
48521             return;
48522         }
48523         
48524         
48525         
48526         var value=this.getEditor().GetData();
48527         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48528         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48529          
48530     },
48531     
48532     setSize : function(w,h) {
48533         
48534         
48535         
48536         //if (this.frame && this.frame.dom.style.display == 'none') {
48537         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48538         //    return;
48539         //}
48540         //if(!this.el || !this.getEditor()) {
48541         //    this.setSize.defer(100,this, [w,h]); 
48542         //    return;
48543         //}
48544         
48545         
48546         
48547         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48548         
48549         this.frame.dom.setAttribute('width', w);
48550         this.frame.dom.setAttribute('height', h);
48551         this.frame.setSize(w,h);
48552         
48553     },
48554     
48555     toggleSourceEdit : function(value) {
48556         
48557       
48558          
48559         this.el.dom.style.display = value ? '' : 'none';
48560         this.frame.dom.style.display = value ?  'none' : '';
48561         
48562     },
48563     
48564     
48565     focus: function(tag)
48566     {
48567         if (this.frame.dom.style.display == 'none') {
48568             return Roo.form.FCKeditor.superclass.focus.call(this);
48569         }
48570         if(!this.el || !this.getEditor()) {
48571             this.focus.defer(100,this, [tag]); 
48572             return;
48573         }
48574         
48575         
48576         
48577         
48578         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48579         this.getEditor().Focus();
48580         if (tgs.length) {
48581             if (!this.getEditor().Selection.GetSelection()) {
48582                 this.focus.defer(100,this, [tag]); 
48583                 return;
48584             }
48585             
48586             
48587             var r = this.getEditor().EditorDocument.createRange();
48588             r.setStart(tgs[0],0);
48589             r.setEnd(tgs[0],0);
48590             this.getEditor().Selection.GetSelection().removeAllRanges();
48591             this.getEditor().Selection.GetSelection().addRange(r);
48592             this.getEditor().Focus();
48593         }
48594         
48595     },
48596     
48597     
48598     
48599     replaceTextarea : function()
48600     {
48601         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48602             return ;
48603         }
48604         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48605         //{
48606             // We must check the elements firstly using the Id and then the name.
48607         var oTextarea = document.getElementById( this.getId() );
48608         
48609         var colElementsByName = document.getElementsByName( this.getId() ) ;
48610          
48611         oTextarea.style.display = 'none' ;
48612
48613         if ( oTextarea.tabIndex ) {            
48614             this.TabIndex = oTextarea.tabIndex ;
48615         }
48616         
48617         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48618         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48619         this.frame = Roo.get(this.getId() + '___Frame')
48620     },
48621     
48622     _getConfigHtml : function()
48623     {
48624         var sConfig = '' ;
48625
48626         for ( var o in this.fckconfig ) {
48627             sConfig += sConfig.length > 0  ? '&amp;' : '';
48628             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48629         }
48630
48631         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48632     },
48633     
48634     
48635     _getIFrameHtml : function()
48636     {
48637         var sFile = 'fckeditor.html' ;
48638         /* no idea what this is about..
48639         try
48640         {
48641             if ( (/fcksource=true/i).test( window.top.location.search ) )
48642                 sFile = 'fckeditor.original.html' ;
48643         }
48644         catch (e) { 
48645         */
48646
48647         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48648         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48649         
48650         
48651         var html = '<iframe id="' + this.getId() +
48652             '___Frame" src="' + sLink +
48653             '" width="' + this.width +
48654             '" height="' + this.height + '"' +
48655             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48656             ' frameborder="0" scrolling="no"></iframe>' ;
48657
48658         return html ;
48659     },
48660     
48661     _insertHtmlBefore : function( html, element )
48662     {
48663         if ( element.insertAdjacentHTML )       {
48664             // IE
48665             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48666         } else { // Gecko
48667             var oRange = document.createRange() ;
48668             oRange.setStartBefore( element ) ;
48669             var oFragment = oRange.createContextualFragment( html );
48670             element.parentNode.insertBefore( oFragment, element ) ;
48671         }
48672     }
48673     
48674     
48675   
48676     
48677     
48678     
48679     
48680
48681 });
48682
48683 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48684
48685 function FCKeditor_OnComplete(editorInstance){
48686     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48687     f.fckEditor = editorInstance;
48688     //console.log("loaded");
48689     f.fireEvent('editorinit', f, editorInstance);
48690
48691   
48692
48693  
48694
48695
48696
48697
48698
48699
48700
48701
48702
48703
48704
48705
48706
48707
48708
48709 //<script type="text/javascript">
48710 /**
48711  * @class Roo.form.GridField
48712  * @extends Roo.form.Field
48713  * Embed a grid (or editable grid into a form)
48714  * STATUS ALPHA
48715  * 
48716  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48717  * it needs 
48718  * xgrid.store = Roo.data.Store
48719  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48720  * xgrid.store.reader = Roo.data.JsonReader 
48721  * 
48722  * 
48723  * @constructor
48724  * Creates a new GridField
48725  * @param {Object} config Configuration options
48726  */
48727 Roo.form.GridField = function(config){
48728     Roo.form.GridField.superclass.constructor.call(this, config);
48729      
48730 };
48731
48732 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48733     /**
48734      * @cfg {Number} width  - used to restrict width of grid..
48735      */
48736     width : 100,
48737     /**
48738      * @cfg {Number} height - used to restrict height of grid..
48739      */
48740     height : 50,
48741      /**
48742      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48743          * 
48744          *}
48745      */
48746     xgrid : false, 
48747     /**
48748      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48749      * {tag: "input", type: "checkbox", autocomplete: "off"})
48750      */
48751    // defaultAutoCreate : { tag: 'div' },
48752     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48753     /**
48754      * @cfg {String} addTitle Text to include for adding a title.
48755      */
48756     addTitle : false,
48757     //
48758     onResize : function(){
48759         Roo.form.Field.superclass.onResize.apply(this, arguments);
48760     },
48761
48762     initEvents : function(){
48763         // Roo.form.Checkbox.superclass.initEvents.call(this);
48764         // has no events...
48765        
48766     },
48767
48768
48769     getResizeEl : function(){
48770         return this.wrap;
48771     },
48772
48773     getPositionEl : function(){
48774         return this.wrap;
48775     },
48776
48777     // private
48778     onRender : function(ct, position){
48779         
48780         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48781         var style = this.style;
48782         delete this.style;
48783         
48784         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48785         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48786         this.viewEl = this.wrap.createChild({ tag: 'div' });
48787         if (style) {
48788             this.viewEl.applyStyles(style);
48789         }
48790         if (this.width) {
48791             this.viewEl.setWidth(this.width);
48792         }
48793         if (this.height) {
48794             this.viewEl.setHeight(this.height);
48795         }
48796         //if(this.inputValue !== undefined){
48797         //this.setValue(this.value);
48798         
48799         
48800         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48801         
48802         
48803         this.grid.render();
48804         this.grid.getDataSource().on('remove', this.refreshValue, this);
48805         this.grid.getDataSource().on('update', this.refreshValue, this);
48806         this.grid.on('afteredit', this.refreshValue, this);
48807  
48808     },
48809      
48810     
48811     /**
48812      * Sets the value of the item. 
48813      * @param {String} either an object  or a string..
48814      */
48815     setValue : function(v){
48816         //this.value = v;
48817         v = v || []; // empty set..
48818         // this does not seem smart - it really only affects memoryproxy grids..
48819         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48820             var ds = this.grid.getDataSource();
48821             // assumes a json reader..
48822             var data = {}
48823             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48824             ds.loadData( data);
48825         }
48826         // clear selection so it does not get stale.
48827         if (this.grid.sm) { 
48828             this.grid.sm.clearSelections();
48829         }
48830         
48831         Roo.form.GridField.superclass.setValue.call(this, v);
48832         this.refreshValue();
48833         // should load data in the grid really....
48834     },
48835     
48836     // private
48837     refreshValue: function() {
48838          var val = [];
48839         this.grid.getDataSource().each(function(r) {
48840             val.push(r.data);
48841         });
48842         this.el.dom.value = Roo.encode(val);
48843     }
48844     
48845      
48846     
48847     
48848 });/*
48849  * Based on:
48850  * Ext JS Library 1.1.1
48851  * Copyright(c) 2006-2007, Ext JS, LLC.
48852  *
48853  * Originally Released Under LGPL - original licence link has changed is not relivant.
48854  *
48855  * Fork - LGPL
48856  * <script type="text/javascript">
48857  */
48858 /**
48859  * @class Roo.form.DisplayField
48860  * @extends Roo.form.Field
48861  * A generic Field to display non-editable data.
48862  * @cfg {Boolean} closable (true|false) default false
48863  * @constructor
48864  * Creates a new Display Field item.
48865  * @param {Object} config Configuration options
48866  */
48867 Roo.form.DisplayField = function(config){
48868     Roo.form.DisplayField.superclass.constructor.call(this, config);
48869     
48870     this.addEvents({
48871         /**
48872          * @event close
48873          * Fires after the click the close btn
48874              * @param {Roo.form.DisplayField} this
48875              */
48876         close : true
48877     });
48878 };
48879
48880 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48881     inputType:      'hidden',
48882     allowBlank:     true,
48883     readOnly:         true,
48884     
48885  
48886     /**
48887      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48888      */
48889     focusClass : undefined,
48890     /**
48891      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48892      */
48893     fieldClass: 'x-form-field',
48894     
48895      /**
48896      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48897      */
48898     valueRenderer: undefined,
48899     
48900     width: 100,
48901     /**
48902      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48903      * {tag: "input", type: "checkbox", autocomplete: "off"})
48904      */
48905      
48906  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48907  
48908     closable : false,
48909     
48910     onResize : function(){
48911         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48912         
48913     },
48914
48915     initEvents : function(){
48916         // Roo.form.Checkbox.superclass.initEvents.call(this);
48917         // has no events...
48918         
48919         if(this.closable){
48920             this.closeEl.on('click', this.onClose, this);
48921         }
48922        
48923     },
48924
48925
48926     getResizeEl : function(){
48927         return this.wrap;
48928     },
48929
48930     getPositionEl : function(){
48931         return this.wrap;
48932     },
48933
48934     // private
48935     onRender : function(ct, position){
48936         
48937         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48938         //if(this.inputValue !== undefined){
48939         this.wrap = this.el.wrap();
48940         
48941         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48942         
48943         if(this.closable){
48944             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48945         }
48946         
48947         if (this.bodyStyle) {
48948             this.viewEl.applyStyles(this.bodyStyle);
48949         }
48950         //this.viewEl.setStyle('padding', '2px');
48951         
48952         this.setValue(this.value);
48953         
48954     },
48955 /*
48956     // private
48957     initValue : Roo.emptyFn,
48958
48959   */
48960
48961         // private
48962     onClick : function(){
48963         
48964     },
48965
48966     /**
48967      * Sets the checked state of the checkbox.
48968      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48969      */
48970     setValue : function(v){
48971         this.value = v;
48972         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48973         // this might be called before we have a dom element..
48974         if (!this.viewEl) {
48975             return;
48976         }
48977         this.viewEl.dom.innerHTML = html;
48978         Roo.form.DisplayField.superclass.setValue.call(this, v);
48979
48980     },
48981     
48982     onClose : function(e)
48983     {
48984         e.preventDefault();
48985         
48986         this.fireEvent('close', this);
48987     }
48988 });/*
48989  * 
48990  * Licence- LGPL
48991  * 
48992  */
48993
48994 /**
48995  * @class Roo.form.DayPicker
48996  * @extends Roo.form.Field
48997  * A Day picker show [M] [T] [W] ....
48998  * @constructor
48999  * Creates a new Day Picker
49000  * @param {Object} config Configuration options
49001  */
49002 Roo.form.DayPicker= function(config){
49003     Roo.form.DayPicker.superclass.constructor.call(this, config);
49004      
49005 };
49006
49007 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49008     /**
49009      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49010      */
49011     focusClass : undefined,
49012     /**
49013      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49014      */
49015     fieldClass: "x-form-field",
49016    
49017     /**
49018      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49019      * {tag: "input", type: "checkbox", autocomplete: "off"})
49020      */
49021     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49022     
49023    
49024     actionMode : 'viewEl', 
49025     //
49026     // private
49027  
49028     inputType : 'hidden',
49029     
49030      
49031     inputElement: false, // real input element?
49032     basedOn: false, // ????
49033     
49034     isFormField: true, // not sure where this is needed!!!!
49035
49036     onResize : function(){
49037         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49038         if(!this.boxLabel){
49039             this.el.alignTo(this.wrap, 'c-c');
49040         }
49041     },
49042
49043     initEvents : function(){
49044         Roo.form.Checkbox.superclass.initEvents.call(this);
49045         this.el.on("click", this.onClick,  this);
49046         this.el.on("change", this.onClick,  this);
49047     },
49048
49049
49050     getResizeEl : function(){
49051         return this.wrap;
49052     },
49053
49054     getPositionEl : function(){
49055         return this.wrap;
49056     },
49057
49058     
49059     // private
49060     onRender : function(ct, position){
49061         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49062        
49063         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49064         
49065         var r1 = '<table><tr>';
49066         var r2 = '<tr class="x-form-daypick-icons">';
49067         for (var i=0; i < 7; i++) {
49068             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49069             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49070         }
49071         
49072         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49073         viewEl.select('img').on('click', this.onClick, this);
49074         this.viewEl = viewEl;   
49075         
49076         
49077         // this will not work on Chrome!!!
49078         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49079         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49080         
49081         
49082           
49083
49084     },
49085
49086     // private
49087     initValue : Roo.emptyFn,
49088
49089     /**
49090      * Returns the checked state of the checkbox.
49091      * @return {Boolean} True if checked, else false
49092      */
49093     getValue : function(){
49094         return this.el.dom.value;
49095         
49096     },
49097
49098         // private
49099     onClick : function(e){ 
49100         //this.setChecked(!this.checked);
49101         Roo.get(e.target).toggleClass('x-menu-item-checked');
49102         this.refreshValue();
49103         //if(this.el.dom.checked != this.checked){
49104         //    this.setValue(this.el.dom.checked);
49105        // }
49106     },
49107     
49108     // private
49109     refreshValue : function()
49110     {
49111         var val = '';
49112         this.viewEl.select('img',true).each(function(e,i,n)  {
49113             val += e.is(".x-menu-item-checked") ? String(n) : '';
49114         });
49115         this.setValue(val, true);
49116     },
49117
49118     /**
49119      * Sets the checked state of the checkbox.
49120      * On is always based on a string comparison between inputValue and the param.
49121      * @param {Boolean/String} value - the value to set 
49122      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49123      */
49124     setValue : function(v,suppressEvent){
49125         if (!this.el.dom) {
49126             return;
49127         }
49128         var old = this.el.dom.value ;
49129         this.el.dom.value = v;
49130         if (suppressEvent) {
49131             return ;
49132         }
49133          
49134         // update display..
49135         this.viewEl.select('img',true).each(function(e,i,n)  {
49136             
49137             var on = e.is(".x-menu-item-checked");
49138             var newv = v.indexOf(String(n)) > -1;
49139             if (on != newv) {
49140                 e.toggleClass('x-menu-item-checked');
49141             }
49142             
49143         });
49144         
49145         
49146         this.fireEvent('change', this, v, old);
49147         
49148         
49149     },
49150    
49151     // handle setting of hidden value by some other method!!?!?
49152     setFromHidden: function()
49153     {
49154         if(!this.el){
49155             return;
49156         }
49157         //console.log("SET FROM HIDDEN");
49158         //alert('setFrom hidden');
49159         this.setValue(this.el.dom.value);
49160     },
49161     
49162     onDestroy : function()
49163     {
49164         if(this.viewEl){
49165             Roo.get(this.viewEl).remove();
49166         }
49167          
49168         Roo.form.DayPicker.superclass.onDestroy.call(this);
49169     }
49170
49171 });/*
49172  * RooJS Library 1.1.1
49173  * Copyright(c) 2008-2011  Alan Knowles
49174  *
49175  * License - LGPL
49176  */
49177  
49178
49179 /**
49180  * @class Roo.form.ComboCheck
49181  * @extends Roo.form.ComboBox
49182  * A combobox for multiple select items.
49183  *
49184  * FIXME - could do with a reset button..
49185  * 
49186  * @constructor
49187  * Create a new ComboCheck
49188  * @param {Object} config Configuration options
49189  */
49190 Roo.form.ComboCheck = function(config){
49191     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49192     // should verify some data...
49193     // like
49194     // hiddenName = required..
49195     // displayField = required
49196     // valudField == required
49197     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49198     var _t = this;
49199     Roo.each(req, function(e) {
49200         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49201             throw "Roo.form.ComboCheck : missing value for: " + e;
49202         }
49203     });
49204     
49205     
49206 };
49207
49208 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49209      
49210      
49211     editable : false,
49212      
49213     selectedClass: 'x-menu-item-checked', 
49214     
49215     // private
49216     onRender : function(ct, position){
49217         var _t = this;
49218         
49219         
49220         
49221         if(!this.tpl){
49222             var cls = 'x-combo-list';
49223
49224             
49225             this.tpl =  new Roo.Template({
49226                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49227                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49228                    '<span>{' + this.displayField + '}</span>' +
49229                     '</div>' 
49230                 
49231             });
49232         }
49233  
49234         
49235         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49236         this.view.singleSelect = false;
49237         this.view.multiSelect = true;
49238         this.view.toggleSelect = true;
49239         this.pageTb.add(new Roo.Toolbar.Fill(), {
49240             
49241             text: 'Done',
49242             handler: function()
49243             {
49244                 _t.collapse();
49245             }
49246         });
49247     },
49248     
49249     onViewOver : function(e, t){
49250         // do nothing...
49251         return;
49252         
49253     },
49254     
49255     onViewClick : function(doFocus,index){
49256         return;
49257         
49258     },
49259     select: function () {
49260         //Roo.log("SELECT CALLED");
49261     },
49262      
49263     selectByValue : function(xv, scrollIntoView){
49264         var ar = this.getValueArray();
49265         var sels = [];
49266         
49267         Roo.each(ar, function(v) {
49268             if(v === undefined || v === null){
49269                 return;
49270             }
49271             var r = this.findRecord(this.valueField, v);
49272             if(r){
49273                 sels.push(this.store.indexOf(r))
49274                 
49275             }
49276         },this);
49277         this.view.select(sels);
49278         return false;
49279     },
49280     
49281     
49282     
49283     onSelect : function(record, index){
49284        // Roo.log("onselect Called");
49285        // this is only called by the clear button now..
49286         this.view.clearSelections();
49287         this.setValue('[]');
49288         if (this.value != this.valueBefore) {
49289             this.fireEvent('change', this, this.value, this.valueBefore);
49290             this.valueBefore = this.value;
49291         }
49292     },
49293     getValueArray : function()
49294     {
49295         var ar = [] ;
49296         
49297         try {
49298             //Roo.log(this.value);
49299             if (typeof(this.value) == 'undefined') {
49300                 return [];
49301             }
49302             var ar = Roo.decode(this.value);
49303             return  ar instanceof Array ? ar : []; //?? valid?
49304             
49305         } catch(e) {
49306             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49307             return [];
49308         }
49309          
49310     },
49311     expand : function ()
49312     {
49313         
49314         Roo.form.ComboCheck.superclass.expand.call(this);
49315         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49316         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49317         
49318
49319     },
49320     
49321     collapse : function(){
49322         Roo.form.ComboCheck.superclass.collapse.call(this);
49323         var sl = this.view.getSelectedIndexes();
49324         var st = this.store;
49325         var nv = [];
49326         var tv = [];
49327         var r;
49328         Roo.each(sl, function(i) {
49329             r = st.getAt(i);
49330             nv.push(r.get(this.valueField));
49331         },this);
49332         this.setValue(Roo.encode(nv));
49333         if (this.value != this.valueBefore) {
49334
49335             this.fireEvent('change', this, this.value, this.valueBefore);
49336             this.valueBefore = this.value;
49337         }
49338         
49339     },
49340     
49341     setValue : function(v){
49342         // Roo.log(v);
49343         this.value = v;
49344         
49345         var vals = this.getValueArray();
49346         var tv = [];
49347         Roo.each(vals, function(k) {
49348             var r = this.findRecord(this.valueField, k);
49349             if(r){
49350                 tv.push(r.data[this.displayField]);
49351             }else if(this.valueNotFoundText !== undefined){
49352                 tv.push( this.valueNotFoundText );
49353             }
49354         },this);
49355        // Roo.log(tv);
49356         
49357         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49358         this.hiddenField.value = v;
49359         this.value = v;
49360     }
49361     
49362 });/*
49363  * Based on:
49364  * Ext JS Library 1.1.1
49365  * Copyright(c) 2006-2007, Ext JS, LLC.
49366  *
49367  * Originally Released Under LGPL - original licence link has changed is not relivant.
49368  *
49369  * Fork - LGPL
49370  * <script type="text/javascript">
49371  */
49372  
49373 /**
49374  * @class Roo.form.Signature
49375  * @extends Roo.form.Field
49376  * Signature field.  
49377  * @constructor
49378  * 
49379  * @param {Object} config Configuration options
49380  */
49381
49382 Roo.form.Signature = function(config){
49383     Roo.form.Signature.superclass.constructor.call(this, config);
49384     
49385     this.addEvents({// not in used??
49386          /**
49387          * @event confirm
49388          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49389              * @param {Roo.form.Signature} combo This combo box
49390              */
49391         'confirm' : true,
49392         /**
49393          * @event reset
49394          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49395              * @param {Roo.form.ComboBox} combo This combo box
49396              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49397              */
49398         'reset' : true
49399     });
49400 };
49401
49402 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49403     /**
49404      * @cfg {Object} labels Label to use when rendering a form.
49405      * defaults to 
49406      * labels : { 
49407      *      clear : "Clear",
49408      *      confirm : "Confirm"
49409      *  }
49410      */
49411     labels : { 
49412         clear : "Clear",
49413         confirm : "Confirm"
49414     },
49415     /**
49416      * @cfg {Number} width The signature panel width (defaults to 300)
49417      */
49418     width: 300,
49419     /**
49420      * @cfg {Number} height The signature panel height (defaults to 100)
49421      */
49422     height : 100,
49423     /**
49424      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49425      */
49426     allowBlank : false,
49427     
49428     //private
49429     // {Object} signPanel The signature SVG panel element (defaults to {})
49430     signPanel : {},
49431     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49432     isMouseDown : false,
49433     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49434     isConfirmed : false,
49435     // {String} signatureTmp SVG mapping string (defaults to empty string)
49436     signatureTmp : '',
49437     
49438     
49439     defaultAutoCreate : { // modified by initCompnoent..
49440         tag: "input",
49441         type:"hidden"
49442     },
49443
49444     // private
49445     onRender : function(ct, position){
49446         
49447         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49448         
49449         this.wrap = this.el.wrap({
49450             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49451         });
49452         
49453         this.createToolbar(this);
49454         this.signPanel = this.wrap.createChild({
49455                 tag: 'div',
49456                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49457             }, this.el
49458         );
49459             
49460         this.svgID = Roo.id();
49461         this.svgEl = this.signPanel.createChild({
49462               xmlns : 'http://www.w3.org/2000/svg',
49463               tag : 'svg',
49464               id : this.svgID + "-svg",
49465               width: this.width,
49466               height: this.height,
49467               viewBox: '0 0 '+this.width+' '+this.height,
49468               cn : [
49469                 {
49470                     tag: "rect",
49471                     id: this.svgID + "-svg-r",
49472                     width: this.width,
49473                     height: this.height,
49474                     fill: "#ffa"
49475                 },
49476                 {
49477                     tag: "line",
49478                     id: this.svgID + "-svg-l",
49479                     x1: "0", // start
49480                     y1: (this.height*0.8), // start set the line in 80% of height
49481                     x2: this.width, // end
49482                     y2: (this.height*0.8), // end set the line in 80% of height
49483                     'stroke': "#666",
49484                     'stroke-width': "1",
49485                     'stroke-dasharray': "3",
49486                     'shape-rendering': "crispEdges",
49487                     'pointer-events': "none"
49488                 },
49489                 {
49490                     tag: "path",
49491                     id: this.svgID + "-svg-p",
49492                     'stroke': "navy",
49493                     'stroke-width': "3",
49494                     'fill': "none",
49495                     'pointer-events': 'none'
49496                 }
49497               ]
49498         });
49499         this.createSVG();
49500         this.svgBox = this.svgEl.dom.getScreenCTM();
49501     },
49502     createSVG : function(){ 
49503         var svg = this.signPanel;
49504         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49505         var t = this;
49506
49507         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49508         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49509         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49510         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49511         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49512         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49513         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49514         
49515     },
49516     isTouchEvent : function(e){
49517         return e.type.match(/^touch/);
49518     },
49519     getCoords : function (e) {
49520         var pt    = this.svgEl.dom.createSVGPoint();
49521         pt.x = e.clientX; 
49522         pt.y = e.clientY;
49523         if (this.isTouchEvent(e)) {
49524             pt.x =  e.targetTouches[0].clientX;
49525             pt.y = e.targetTouches[0].clientY;
49526         }
49527         var a = this.svgEl.dom.getScreenCTM();
49528         var b = a.inverse();
49529         var mx = pt.matrixTransform(b);
49530         return mx.x + ',' + mx.y;
49531     },
49532     //mouse event headler 
49533     down : function (e) {
49534         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49535         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49536         
49537         this.isMouseDown = true;
49538         
49539         e.preventDefault();
49540     },
49541     move : function (e) {
49542         if (this.isMouseDown) {
49543             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49544             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49545         }
49546         
49547         e.preventDefault();
49548     },
49549     up : function (e) {
49550         this.isMouseDown = false;
49551         var sp = this.signatureTmp.split(' ');
49552         
49553         if(sp.length > 1){
49554             if(!sp[sp.length-2].match(/^L/)){
49555                 sp.pop();
49556                 sp.pop();
49557                 sp.push("");
49558                 this.signatureTmp = sp.join(" ");
49559             }
49560         }
49561         if(this.getValue() != this.signatureTmp){
49562             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49563             this.isConfirmed = false;
49564         }
49565         e.preventDefault();
49566     },
49567     
49568     /**
49569      * Protected method that will not generally be called directly. It
49570      * is called when the editor creates its toolbar. Override this method if you need to
49571      * add custom toolbar buttons.
49572      * @param {HtmlEditor} editor
49573      */
49574     createToolbar : function(editor){
49575          function btn(id, toggle, handler){
49576             var xid = fid + '-'+ id ;
49577             return {
49578                 id : xid,
49579                 cmd : id,
49580                 cls : 'x-btn-icon x-edit-'+id,
49581                 enableToggle:toggle !== false,
49582                 scope: editor, // was editor...
49583                 handler:handler||editor.relayBtnCmd,
49584                 clickEvent:'mousedown',
49585                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49586                 tabIndex:-1
49587             };
49588         }
49589         
49590         
49591         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49592         this.tb = tb;
49593         this.tb.add(
49594            {
49595                 cls : ' x-signature-btn x-signature-'+id,
49596                 scope: editor, // was editor...
49597                 handler: this.reset,
49598                 clickEvent:'mousedown',
49599                 text: this.labels.clear
49600             },
49601             {
49602                  xtype : 'Fill',
49603                  xns: Roo.Toolbar
49604             }, 
49605             {
49606                 cls : '  x-signature-btn x-signature-'+id,
49607                 scope: editor, // was editor...
49608                 handler: this.confirmHandler,
49609                 clickEvent:'mousedown',
49610                 text: this.labels.confirm
49611             }
49612         );
49613     
49614     },
49615     //public
49616     /**
49617      * when user is clicked confirm then show this image.....
49618      * 
49619      * @return {String} Image Data URI
49620      */
49621     getImageDataURI : function(){
49622         var svg = this.svgEl.dom.parentNode.innerHTML;
49623         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49624         return src; 
49625     },
49626     /**
49627      * 
49628      * @return {Boolean} this.isConfirmed
49629      */
49630     getConfirmed : function(){
49631         return this.isConfirmed;
49632     },
49633     /**
49634      * 
49635      * @return {Number} this.width
49636      */
49637     getWidth : function(){
49638         return this.width;
49639     },
49640     /**
49641      * 
49642      * @return {Number} this.height
49643      */
49644     getHeight : function(){
49645         return this.height;
49646     },
49647     // private
49648     getSignature : function(){
49649         return this.signatureTmp;
49650     },
49651     // private
49652     reset : function(){
49653         this.signatureTmp = '';
49654         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49655         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49656         this.isConfirmed = false;
49657         Roo.form.Signature.superclass.reset.call(this);
49658     },
49659     setSignature : function(s){
49660         this.signatureTmp = s;
49661         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49662         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49663         this.setValue(s);
49664         this.isConfirmed = false;
49665         Roo.form.Signature.superclass.reset.call(this);
49666     }, 
49667     test : function(){
49668 //        Roo.log(this.signPanel.dom.contentWindow.up())
49669     },
49670     //private
49671     setConfirmed : function(){
49672         
49673         
49674         
49675 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49676     },
49677     // private
49678     confirmHandler : function(){
49679         if(!this.getSignature()){
49680             return;
49681         }
49682         
49683         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49684         this.setValue(this.getSignature());
49685         this.isConfirmed = true;
49686         
49687         this.fireEvent('confirm', this);
49688     },
49689     // private
49690     // Subclasses should provide the validation implementation by overriding this
49691     validateValue : function(value){
49692         if(this.allowBlank){
49693             return true;
49694         }
49695         
49696         if(this.isConfirmed){
49697             return true;
49698         }
49699         return false;
49700     }
49701 });/*
49702  * Based on:
49703  * Ext JS Library 1.1.1
49704  * Copyright(c) 2006-2007, Ext JS, LLC.
49705  *
49706  * Originally Released Under LGPL - original licence link has changed is not relivant.
49707  *
49708  * Fork - LGPL
49709  * <script type="text/javascript">
49710  */
49711  
49712
49713 /**
49714  * @class Roo.form.ComboBox
49715  * @extends Roo.form.TriggerField
49716  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49717  * @constructor
49718  * Create a new ComboBox.
49719  * @param {Object} config Configuration options
49720  */
49721 Roo.form.Select = function(config){
49722     Roo.form.Select.superclass.constructor.call(this, config);
49723      
49724 };
49725
49726 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49727     /**
49728      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49729      */
49730     /**
49731      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49732      * rendering into an Roo.Editor, defaults to false)
49733      */
49734     /**
49735      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49736      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49737      */
49738     /**
49739      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49740      */
49741     /**
49742      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49743      * the dropdown list (defaults to undefined, with no header element)
49744      */
49745
49746      /**
49747      * @cfg {String/Roo.Template} tpl The template to use to render the output
49748      */
49749      
49750     // private
49751     defaultAutoCreate : {tag: "select"  },
49752     /**
49753      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49754      */
49755     listWidth: undefined,
49756     /**
49757      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49758      * mode = 'remote' or 'text' if mode = 'local')
49759      */
49760     displayField: undefined,
49761     /**
49762      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49763      * mode = 'remote' or 'value' if mode = 'local'). 
49764      * Note: use of a valueField requires the user make a selection
49765      * in order for a value to be mapped.
49766      */
49767     valueField: undefined,
49768     
49769     
49770     /**
49771      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49772      * field's data value (defaults to the underlying DOM element's name)
49773      */
49774     hiddenName: undefined,
49775     /**
49776      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49777      */
49778     listClass: '',
49779     /**
49780      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49781      */
49782     selectedClass: 'x-combo-selected',
49783     /**
49784      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49785      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49786      * which displays a downward arrow icon).
49787      */
49788     triggerClass : 'x-form-arrow-trigger',
49789     /**
49790      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49791      */
49792     shadow:'sides',
49793     /**
49794      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49795      * anchor positions (defaults to 'tl-bl')
49796      */
49797     listAlign: 'tl-bl?',
49798     /**
49799      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49800      */
49801     maxHeight: 300,
49802     /**
49803      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49804      * query specified by the allQuery config option (defaults to 'query')
49805      */
49806     triggerAction: 'query',
49807     /**
49808      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49809      * (defaults to 4, does not apply if editable = false)
49810      */
49811     minChars : 4,
49812     /**
49813      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49814      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49815      */
49816     typeAhead: false,
49817     /**
49818      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49819      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49820      */
49821     queryDelay: 500,
49822     /**
49823      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49824      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49825      */
49826     pageSize: 0,
49827     /**
49828      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49829      * when editable = true (defaults to false)
49830      */
49831     selectOnFocus:false,
49832     /**
49833      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49834      */
49835     queryParam: 'query',
49836     /**
49837      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49838      * when mode = 'remote' (defaults to 'Loading...')
49839      */
49840     loadingText: 'Loading...',
49841     /**
49842      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49843      */
49844     resizable: false,
49845     /**
49846      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49847      */
49848     handleHeight : 8,
49849     /**
49850      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49851      * traditional select (defaults to true)
49852      */
49853     editable: true,
49854     /**
49855      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49856      */
49857     allQuery: '',
49858     /**
49859      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49860      */
49861     mode: 'remote',
49862     /**
49863      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49864      * listWidth has a higher value)
49865      */
49866     minListWidth : 70,
49867     /**
49868      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49869      * allow the user to set arbitrary text into the field (defaults to false)
49870      */
49871     forceSelection:false,
49872     /**
49873      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49874      * if typeAhead = true (defaults to 250)
49875      */
49876     typeAheadDelay : 250,
49877     /**
49878      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49879      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49880      */
49881     valueNotFoundText : undefined,
49882     
49883     /**
49884      * @cfg {String} defaultValue The value displayed after loading the store.
49885      */
49886     defaultValue: '',
49887     
49888     /**
49889      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49890      */
49891     blockFocus : false,
49892     
49893     /**
49894      * @cfg {Boolean} disableClear Disable showing of clear button.
49895      */
49896     disableClear : false,
49897     /**
49898      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49899      */
49900     alwaysQuery : false,
49901     
49902     //private
49903     addicon : false,
49904     editicon: false,
49905     
49906     // element that contains real text value.. (when hidden is used..)
49907      
49908     // private
49909     onRender : function(ct, position){
49910         Roo.form.Field.prototype.onRender.call(this, ct, position);
49911         
49912         if(this.store){
49913             this.store.on('beforeload', this.onBeforeLoad, this);
49914             this.store.on('load', this.onLoad, this);
49915             this.store.on('loadexception', this.onLoadException, this);
49916             this.store.load({});
49917         }
49918         
49919         
49920         
49921     },
49922
49923     // private
49924     initEvents : function(){
49925         //Roo.form.ComboBox.superclass.initEvents.call(this);
49926  
49927     },
49928
49929     onDestroy : function(){
49930        
49931         if(this.store){
49932             this.store.un('beforeload', this.onBeforeLoad, this);
49933             this.store.un('load', this.onLoad, this);
49934             this.store.un('loadexception', this.onLoadException, this);
49935         }
49936         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49937     },
49938
49939     // private
49940     fireKey : function(e){
49941         if(e.isNavKeyPress() && !this.list.isVisible()){
49942             this.fireEvent("specialkey", this, e);
49943         }
49944     },
49945
49946     // private
49947     onResize: function(w, h){
49948         
49949         return; 
49950     
49951         
49952     },
49953
49954     /**
49955      * Allow or prevent the user from directly editing the field text.  If false is passed,
49956      * the user will only be able to select from the items defined in the dropdown list.  This method
49957      * is the runtime equivalent of setting the 'editable' config option at config time.
49958      * @param {Boolean} value True to allow the user to directly edit the field text
49959      */
49960     setEditable : function(value){
49961          
49962     },
49963
49964     // private
49965     onBeforeLoad : function(){
49966         
49967         Roo.log("Select before load");
49968         return;
49969     
49970         this.innerList.update(this.loadingText ?
49971                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49972         //this.restrictHeight();
49973         this.selectedIndex = -1;
49974     },
49975
49976     // private
49977     onLoad : function(){
49978
49979     
49980         var dom = this.el.dom;
49981         dom.innerHTML = '';
49982          var od = dom.ownerDocument;
49983          
49984         if (this.emptyText) {
49985             var op = od.createElement('option');
49986             op.setAttribute('value', '');
49987             op.innerHTML = String.format('{0}', this.emptyText);
49988             dom.appendChild(op);
49989         }
49990         if(this.store.getCount() > 0){
49991            
49992             var vf = this.valueField;
49993             var df = this.displayField;
49994             this.store.data.each(function(r) {
49995                 // which colmsn to use... testing - cdoe / title..
49996                 var op = od.createElement('option');
49997                 op.setAttribute('value', r.data[vf]);
49998                 op.innerHTML = String.format('{0}', r.data[df]);
49999                 dom.appendChild(op);
50000             });
50001             if (typeof(this.defaultValue != 'undefined')) {
50002                 this.setValue(this.defaultValue);
50003             }
50004             
50005              
50006         }else{
50007             //this.onEmptyResults();
50008         }
50009         //this.el.focus();
50010     },
50011     // private
50012     onLoadException : function()
50013     {
50014         dom.innerHTML = '';
50015             
50016         Roo.log("Select on load exception");
50017         return;
50018     
50019         this.collapse();
50020         Roo.log(this.store.reader.jsonData);
50021         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50022             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50023         }
50024         
50025         
50026     },
50027     // private
50028     onTypeAhead : function(){
50029          
50030     },
50031
50032     // private
50033     onSelect : function(record, index){
50034         Roo.log('on select?');
50035         return;
50036         if(this.fireEvent('beforeselect', this, record, index) !== false){
50037             this.setFromData(index > -1 ? record.data : false);
50038             this.collapse();
50039             this.fireEvent('select', this, record, index);
50040         }
50041     },
50042
50043     /**
50044      * Returns the currently selected field value or empty string if no value is set.
50045      * @return {String} value The selected value
50046      */
50047     getValue : function(){
50048         var dom = this.el.dom;
50049         this.value = dom.options[dom.selectedIndex].value;
50050         return this.value;
50051         
50052     },
50053
50054     /**
50055      * Clears any text/value currently set in the field
50056      */
50057     clearValue : function(){
50058         this.value = '';
50059         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50060         
50061     },
50062
50063     /**
50064      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50065      * will be displayed in the field.  If the value does not match the data value of an existing item,
50066      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50067      * Otherwise the field will be blank (although the value will still be set).
50068      * @param {String} value The value to match
50069      */
50070     setValue : function(v){
50071         var d = this.el.dom;
50072         for (var i =0; i < d.options.length;i++) {
50073             if (v == d.options[i].value) {
50074                 d.selectedIndex = i;
50075                 this.value = v;
50076                 return;
50077             }
50078         }
50079         this.clearValue();
50080     },
50081     /**
50082      * @property {Object} the last set data for the element
50083      */
50084     
50085     lastData : false,
50086     /**
50087      * Sets the value of the field based on a object which is related to the record format for the store.
50088      * @param {Object} value the value to set as. or false on reset?
50089      */
50090     setFromData : function(o){
50091         Roo.log('setfrom data?');
50092          
50093         
50094         
50095     },
50096     // private
50097     reset : function(){
50098         this.clearValue();
50099     },
50100     // private
50101     findRecord : function(prop, value){
50102         
50103         return false;
50104     
50105         var record;
50106         if(this.store.getCount() > 0){
50107             this.store.each(function(r){
50108                 if(r.data[prop] == value){
50109                     record = r;
50110                     return false;
50111                 }
50112                 return true;
50113             });
50114         }
50115         return record;
50116     },
50117     
50118     getName: function()
50119     {
50120         // returns hidden if it's set..
50121         if (!this.rendered) {return ''};
50122         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50123         
50124     },
50125      
50126
50127     
50128
50129     // private
50130     onEmptyResults : function(){
50131         Roo.log('empty results');
50132         //this.collapse();
50133     },
50134
50135     /**
50136      * Returns true if the dropdown list is expanded, else false.
50137      */
50138     isExpanded : function(){
50139         return false;
50140     },
50141
50142     /**
50143      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50144      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50145      * @param {String} value The data value of the item to select
50146      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50147      * selected item if it is not currently in view (defaults to true)
50148      * @return {Boolean} True if the value matched an item in the list, else false
50149      */
50150     selectByValue : function(v, scrollIntoView){
50151         Roo.log('select By Value');
50152         return false;
50153     
50154         if(v !== undefined && v !== null){
50155             var r = this.findRecord(this.valueField || this.displayField, v);
50156             if(r){
50157                 this.select(this.store.indexOf(r), scrollIntoView);
50158                 return true;
50159             }
50160         }
50161         return false;
50162     },
50163
50164     /**
50165      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50166      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50167      * @param {Number} index The zero-based index of the list item to select
50168      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50169      * selected item if it is not currently in view (defaults to true)
50170      */
50171     select : function(index, scrollIntoView){
50172         Roo.log('select ');
50173         return  ;
50174         
50175         this.selectedIndex = index;
50176         this.view.select(index);
50177         if(scrollIntoView !== false){
50178             var el = this.view.getNode(index);
50179             if(el){
50180                 this.innerList.scrollChildIntoView(el, false);
50181             }
50182         }
50183     },
50184
50185       
50186
50187     // private
50188     validateBlur : function(){
50189         
50190         return;
50191         
50192     },
50193
50194     // private
50195     initQuery : function(){
50196         this.doQuery(this.getRawValue());
50197     },
50198
50199     // private
50200     doForce : function(){
50201         if(this.el.dom.value.length > 0){
50202             this.el.dom.value =
50203                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50204              
50205         }
50206     },
50207
50208     /**
50209      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50210      * query allowing the query action to be canceled if needed.
50211      * @param {String} query The SQL query to execute
50212      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50213      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50214      * saved in the current store (defaults to false)
50215      */
50216     doQuery : function(q, forceAll){
50217         
50218         Roo.log('doQuery?');
50219         if(q === undefined || q === null){
50220             q = '';
50221         }
50222         var qe = {
50223             query: q,
50224             forceAll: forceAll,
50225             combo: this,
50226             cancel:false
50227         };
50228         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50229             return false;
50230         }
50231         q = qe.query;
50232         forceAll = qe.forceAll;
50233         if(forceAll === true || (q.length >= this.minChars)){
50234             if(this.lastQuery != q || this.alwaysQuery){
50235                 this.lastQuery = q;
50236                 if(this.mode == 'local'){
50237                     this.selectedIndex = -1;
50238                     if(forceAll){
50239                         this.store.clearFilter();
50240                     }else{
50241                         this.store.filter(this.displayField, q);
50242                     }
50243                     this.onLoad();
50244                 }else{
50245                     this.store.baseParams[this.queryParam] = q;
50246                     this.store.load({
50247                         params: this.getParams(q)
50248                     });
50249                     this.expand();
50250                 }
50251             }else{
50252                 this.selectedIndex = -1;
50253                 this.onLoad();   
50254             }
50255         }
50256     },
50257
50258     // private
50259     getParams : function(q){
50260         var p = {};
50261         //p[this.queryParam] = q;
50262         if(this.pageSize){
50263             p.start = 0;
50264             p.limit = this.pageSize;
50265         }
50266         return p;
50267     },
50268
50269     /**
50270      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50271      */
50272     collapse : function(){
50273         
50274     },
50275
50276     // private
50277     collapseIf : function(e){
50278         
50279     },
50280
50281     /**
50282      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50283      */
50284     expand : function(){
50285         
50286     } ,
50287
50288     // private
50289      
50290
50291     /** 
50292     * @cfg {Boolean} grow 
50293     * @hide 
50294     */
50295     /** 
50296     * @cfg {Number} growMin 
50297     * @hide 
50298     */
50299     /** 
50300     * @cfg {Number} growMax 
50301     * @hide 
50302     */
50303     /**
50304      * @hide
50305      * @method autoSize
50306      */
50307     
50308     setWidth : function()
50309     {
50310         
50311     },
50312     getResizeEl : function(){
50313         return this.el;
50314     }
50315 });//<script type="text/javasscript">
50316  
50317
50318 /**
50319  * @class Roo.DDView
50320  * A DnD enabled version of Roo.View.
50321  * @param {Element/String} container The Element in which to create the View.
50322  * @param {String} tpl The template string used to create the markup for each element of the View
50323  * @param {Object} config The configuration properties. These include all the config options of
50324  * {@link Roo.View} plus some specific to this class.<br>
50325  * <p>
50326  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50327  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50328  * <p>
50329  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50330 .x-view-drag-insert-above {
50331         border-top:1px dotted #3366cc;
50332 }
50333 .x-view-drag-insert-below {
50334         border-bottom:1px dotted #3366cc;
50335 }
50336 </code></pre>
50337  * 
50338  */
50339  
50340 Roo.DDView = function(container, tpl, config) {
50341     Roo.DDView.superclass.constructor.apply(this, arguments);
50342     this.getEl().setStyle("outline", "0px none");
50343     this.getEl().unselectable();
50344     if (this.dragGroup) {
50345                 this.setDraggable(this.dragGroup.split(","));
50346     }
50347     if (this.dropGroup) {
50348                 this.setDroppable(this.dropGroup.split(","));
50349     }
50350     if (this.deletable) {
50351         this.setDeletable();
50352     }
50353     this.isDirtyFlag = false;
50354         this.addEvents({
50355                 "drop" : true
50356         });
50357 };
50358
50359 Roo.extend(Roo.DDView, Roo.View, {
50360 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50361 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50362 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50363 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50364
50365         isFormField: true,
50366
50367         reset: Roo.emptyFn,
50368         
50369         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50370
50371         validate: function() {
50372                 return true;
50373         },
50374         
50375         destroy: function() {
50376                 this.purgeListeners();
50377                 this.getEl.removeAllListeners();
50378                 this.getEl().remove();
50379                 if (this.dragZone) {
50380                         if (this.dragZone.destroy) {
50381                                 this.dragZone.destroy();
50382                         }
50383                 }
50384                 if (this.dropZone) {
50385                         if (this.dropZone.destroy) {
50386                                 this.dropZone.destroy();
50387                         }
50388                 }
50389         },
50390
50391 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50392         getName: function() {
50393                 return this.name;
50394         },
50395
50396 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50397         setValue: function(v) {
50398                 if (!this.store) {
50399                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50400                 }
50401                 var data = {};
50402                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50403                 this.store.proxy = new Roo.data.MemoryProxy(data);
50404                 this.store.load();
50405         },
50406
50407 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50408         getValue: function() {
50409                 var result = '(';
50410                 this.store.each(function(rec) {
50411                         result += rec.id + ',';
50412                 });
50413                 return result.substr(0, result.length - 1) + ')';
50414         },
50415         
50416         getIds: function() {
50417                 var i = 0, result = new Array(this.store.getCount());
50418                 this.store.each(function(rec) {
50419                         result[i++] = rec.id;
50420                 });
50421                 return result;
50422         },
50423         
50424         isDirty: function() {
50425                 return this.isDirtyFlag;
50426         },
50427
50428 /**
50429  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50430  *      whole Element becomes the target, and this causes the drop gesture to append.
50431  */
50432     getTargetFromEvent : function(e) {
50433                 var target = e.getTarget();
50434                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50435                 target = target.parentNode;
50436                 }
50437                 if (!target) {
50438                         target = this.el.dom.lastChild || this.el.dom;
50439                 }
50440                 return target;
50441     },
50442
50443 /**
50444  *      Create the drag data which consists of an object which has the property "ddel" as
50445  *      the drag proxy element. 
50446  */
50447     getDragData : function(e) {
50448         var target = this.findItemFromChild(e.getTarget());
50449                 if(target) {
50450                         this.handleSelection(e);
50451                         var selNodes = this.getSelectedNodes();
50452             var dragData = {
50453                 source: this,
50454                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50455                 nodes: selNodes,
50456                 records: []
50457                         };
50458                         var selectedIndices = this.getSelectedIndexes();
50459                         for (var i = 0; i < selectedIndices.length; i++) {
50460                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50461                         }
50462                         if (selNodes.length == 1) {
50463                                 dragData.ddel = target.cloneNode(true); // the div element
50464                         } else {
50465                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50466                                 div.className = 'multi-proxy';
50467                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50468                                         div.appendChild(selNodes[i].cloneNode(true));
50469                                 }
50470                                 dragData.ddel = div;
50471                         }
50472             //console.log(dragData)
50473             //console.log(dragData.ddel.innerHTML)
50474                         return dragData;
50475                 }
50476         //console.log('nodragData')
50477                 return false;
50478     },
50479     
50480 /**     Specify to which ddGroup items in this DDView may be dragged. */
50481     setDraggable: function(ddGroup) {
50482         if (ddGroup instanceof Array) {
50483                 Roo.each(ddGroup, this.setDraggable, this);
50484                 return;
50485         }
50486         if (this.dragZone) {
50487                 this.dragZone.addToGroup(ddGroup);
50488         } else {
50489                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50490                                 containerScroll: true,
50491                                 ddGroup: ddGroup 
50492
50493                         });
50494 //                      Draggability implies selection. DragZone's mousedown selects the element.
50495                         if (!this.multiSelect) { this.singleSelect = true; }
50496
50497 //                      Wire the DragZone's handlers up to methods in *this*
50498                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50499                 }
50500     },
50501
50502 /**     Specify from which ddGroup this DDView accepts drops. */
50503     setDroppable: function(ddGroup) {
50504         if (ddGroup instanceof Array) {
50505                 Roo.each(ddGroup, this.setDroppable, this);
50506                 return;
50507         }
50508         if (this.dropZone) {
50509                 this.dropZone.addToGroup(ddGroup);
50510         } else {
50511                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50512                                 containerScroll: true,
50513                                 ddGroup: ddGroup
50514                         });
50515
50516 //                      Wire the DropZone's handlers up to methods in *this*
50517                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50518                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50519                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50520                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50521                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50522                 }
50523     },
50524
50525 /**     Decide whether to drop above or below a View node. */
50526     getDropPoint : function(e, n, dd){
50527         if (n == this.el.dom) { return "above"; }
50528                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50529                 var c = t + (b - t) / 2;
50530                 var y = Roo.lib.Event.getPageY(e);
50531                 if(y <= c) {
50532                         return "above";
50533                 }else{
50534                         return "below";
50535                 }
50536     },
50537
50538     onNodeEnter : function(n, dd, e, data){
50539                 return false;
50540     },
50541     
50542     onNodeOver : function(n, dd, e, data){
50543                 var pt = this.getDropPoint(e, n, dd);
50544                 // set the insert point style on the target node
50545                 var dragElClass = this.dropNotAllowed;
50546                 if (pt) {
50547                         var targetElClass;
50548                         if (pt == "above"){
50549                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50550                                 targetElClass = "x-view-drag-insert-above";
50551                         } else {
50552                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50553                                 targetElClass = "x-view-drag-insert-below";
50554                         }
50555                         if (this.lastInsertClass != targetElClass){
50556                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50557                                 this.lastInsertClass = targetElClass;
50558                         }
50559                 }
50560                 return dragElClass;
50561         },
50562
50563     onNodeOut : function(n, dd, e, data){
50564                 this.removeDropIndicators(n);
50565     },
50566
50567     onNodeDrop : function(n, dd, e, data){
50568         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50569                 return false;
50570         }
50571         var pt = this.getDropPoint(e, n, dd);
50572                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50573                 if (pt == "below") { insertAt++; }
50574                 for (var i = 0; i < data.records.length; i++) {
50575                         var r = data.records[i];
50576                         var dup = this.store.getById(r.id);
50577                         if (dup && (dd != this.dragZone)) {
50578                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50579                         } else {
50580                                 if (data.copy) {
50581                                         this.store.insert(insertAt++, r.copy());
50582                                 } else {
50583                                         data.source.isDirtyFlag = true;
50584                                         r.store.remove(r);
50585                                         this.store.insert(insertAt++, r);
50586                                 }
50587                                 this.isDirtyFlag = true;
50588                         }
50589                 }
50590                 this.dragZone.cachedTarget = null;
50591                 return true;
50592     },
50593
50594     removeDropIndicators : function(n){
50595                 if(n){
50596                         Roo.fly(n).removeClass([
50597                                 "x-view-drag-insert-above",
50598                                 "x-view-drag-insert-below"]);
50599                         this.lastInsertClass = "_noclass";
50600                 }
50601     },
50602
50603 /**
50604  *      Utility method. Add a delete option to the DDView's context menu.
50605  *      @param {String} imageUrl The URL of the "delete" icon image.
50606  */
50607         setDeletable: function(imageUrl) {
50608                 if (!this.singleSelect && !this.multiSelect) {
50609                         this.singleSelect = true;
50610                 }
50611                 var c = this.getContextMenu();
50612                 this.contextMenu.on("itemclick", function(item) {
50613                         switch (item.id) {
50614                                 case "delete":
50615                                         this.remove(this.getSelectedIndexes());
50616                                         break;
50617                         }
50618                 }, this);
50619                 this.contextMenu.add({
50620                         icon: imageUrl,
50621                         id: "delete",
50622                         text: 'Delete'
50623                 });
50624         },
50625         
50626 /**     Return the context menu for this DDView. */
50627         getContextMenu: function() {
50628                 if (!this.contextMenu) {
50629 //                      Create the View's context menu
50630                         this.contextMenu = new Roo.menu.Menu({
50631                                 id: this.id + "-contextmenu"
50632                         });
50633                         this.el.on("contextmenu", this.showContextMenu, this);
50634                 }
50635                 return this.contextMenu;
50636         },
50637         
50638         disableContextMenu: function() {
50639                 if (this.contextMenu) {
50640                         this.el.un("contextmenu", this.showContextMenu, this);
50641                 }
50642         },
50643
50644         showContextMenu: function(e, item) {
50645         item = this.findItemFromChild(e.getTarget());
50646                 if (item) {
50647                         e.stopEvent();
50648                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50649                         this.contextMenu.showAt(e.getXY());
50650             }
50651     },
50652
50653 /**
50654  *      Remove {@link Roo.data.Record}s at the specified indices.
50655  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50656  */
50657     remove: function(selectedIndices) {
50658                 selectedIndices = [].concat(selectedIndices);
50659                 for (var i = 0; i < selectedIndices.length; i++) {
50660                         var rec = this.store.getAt(selectedIndices[i]);
50661                         this.store.remove(rec);
50662                 }
50663     },
50664
50665 /**
50666  *      Double click fires the event, but also, if this is draggable, and there is only one other
50667  *      related DropZone, it transfers the selected node.
50668  */
50669     onDblClick : function(e){
50670         var item = this.findItemFromChild(e.getTarget());
50671         if(item){
50672             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50673                 return false;
50674             }
50675             if (this.dragGroup) {
50676                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50677                     while (targets.indexOf(this.dropZone) > -1) {
50678                             targets.remove(this.dropZone);
50679                                 }
50680                     if (targets.length == 1) {
50681                                         this.dragZone.cachedTarget = null;
50682                         var el = Roo.get(targets[0].getEl());
50683                         var box = el.getBox(true);
50684                         targets[0].onNodeDrop(el.dom, {
50685                                 target: el.dom,
50686                                 xy: [box.x, box.y + box.height - 1]
50687                         }, null, this.getDragData(e));
50688                     }
50689                 }
50690         }
50691     },
50692     
50693     handleSelection: function(e) {
50694                 this.dragZone.cachedTarget = null;
50695         var item = this.findItemFromChild(e.getTarget());
50696         if (!item) {
50697                 this.clearSelections(true);
50698                 return;
50699         }
50700                 if (item && (this.multiSelect || this.singleSelect)){
50701                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50702                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50703                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50704                                 this.unselect(item);
50705                         } else {
50706                                 this.select(item, this.multiSelect && e.ctrlKey);
50707                                 this.lastSelection = item;
50708                         }
50709                 }
50710     },
50711
50712     onItemClick : function(item, index, e){
50713                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50714                         return false;
50715                 }
50716                 return true;
50717     },
50718
50719     unselect : function(nodeInfo, suppressEvent){
50720                 var node = this.getNode(nodeInfo);
50721                 if(node && this.isSelected(node)){
50722                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50723                                 Roo.fly(node).removeClass(this.selectedClass);
50724                                 this.selections.remove(node);
50725                                 if(!suppressEvent){
50726                                         this.fireEvent("selectionchange", this, this.selections);
50727                                 }
50728                         }
50729                 }
50730     }
50731 });
50732 /*
50733  * Based on:
50734  * Ext JS Library 1.1.1
50735  * Copyright(c) 2006-2007, Ext JS, LLC.
50736  *
50737  * Originally Released Under LGPL - original licence link has changed is not relivant.
50738  *
50739  * Fork - LGPL
50740  * <script type="text/javascript">
50741  */
50742  
50743 /**
50744  * @class Roo.LayoutManager
50745  * @extends Roo.util.Observable
50746  * Base class for layout managers.
50747  */
50748 Roo.LayoutManager = function(container, config){
50749     Roo.LayoutManager.superclass.constructor.call(this);
50750     this.el = Roo.get(container);
50751     // ie scrollbar fix
50752     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50753         document.body.scroll = "no";
50754     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50755         this.el.position('relative');
50756     }
50757     this.id = this.el.id;
50758     this.el.addClass("x-layout-container");
50759     /** false to disable window resize monitoring @type Boolean */
50760     this.monitorWindowResize = true;
50761     this.regions = {};
50762     this.addEvents({
50763         /**
50764          * @event layout
50765          * Fires when a layout is performed. 
50766          * @param {Roo.LayoutManager} this
50767          */
50768         "layout" : true,
50769         /**
50770          * @event regionresized
50771          * Fires when the user resizes a region. 
50772          * @param {Roo.LayoutRegion} region The resized region
50773          * @param {Number} newSize The new size (width for east/west, height for north/south)
50774          */
50775         "regionresized" : true,
50776         /**
50777          * @event regioncollapsed
50778          * Fires when a region is collapsed. 
50779          * @param {Roo.LayoutRegion} region The collapsed region
50780          */
50781         "regioncollapsed" : true,
50782         /**
50783          * @event regionexpanded
50784          * Fires when a region is expanded.  
50785          * @param {Roo.LayoutRegion} region The expanded region
50786          */
50787         "regionexpanded" : true
50788     });
50789     this.updating = false;
50790     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50791 };
50792
50793 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50794     /**
50795      * Returns true if this layout is currently being updated
50796      * @return {Boolean}
50797      */
50798     isUpdating : function(){
50799         return this.updating; 
50800     },
50801     
50802     /**
50803      * Suspend the LayoutManager from doing auto-layouts while
50804      * making multiple add or remove calls
50805      */
50806     beginUpdate : function(){
50807         this.updating = true;    
50808     },
50809     
50810     /**
50811      * Restore auto-layouts and optionally disable the manager from performing a layout
50812      * @param {Boolean} noLayout true to disable a layout update 
50813      */
50814     endUpdate : function(noLayout){
50815         this.updating = false;
50816         if(!noLayout){
50817             this.layout();
50818         }    
50819     },
50820     
50821     layout: function(){
50822         
50823     },
50824     
50825     onRegionResized : function(region, newSize){
50826         this.fireEvent("regionresized", region, newSize);
50827         this.layout();
50828     },
50829     
50830     onRegionCollapsed : function(region){
50831         this.fireEvent("regioncollapsed", region);
50832     },
50833     
50834     onRegionExpanded : function(region){
50835         this.fireEvent("regionexpanded", region);
50836     },
50837         
50838     /**
50839      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50840      * performs box-model adjustments.
50841      * @return {Object} The size as an object {width: (the width), height: (the height)}
50842      */
50843     getViewSize : function(){
50844         var size;
50845         if(this.el.dom != document.body){
50846             size = this.el.getSize();
50847         }else{
50848             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50849         }
50850         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50851         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50852         return size;
50853     },
50854     
50855     /**
50856      * Returns the Element this layout is bound to.
50857      * @return {Roo.Element}
50858      */
50859     getEl : function(){
50860         return this.el;
50861     },
50862     
50863     /**
50864      * Returns the specified region.
50865      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50866      * @return {Roo.LayoutRegion}
50867      */
50868     getRegion : function(target){
50869         return this.regions[target.toLowerCase()];
50870     },
50871     
50872     onWindowResize : function(){
50873         if(this.monitorWindowResize){
50874             this.layout();
50875         }
50876     }
50877 });/*
50878  * Based on:
50879  * Ext JS Library 1.1.1
50880  * Copyright(c) 2006-2007, Ext JS, LLC.
50881  *
50882  * Originally Released Under LGPL - original licence link has changed is not relivant.
50883  *
50884  * Fork - LGPL
50885  * <script type="text/javascript">
50886  */
50887 /**
50888  * @class Roo.BorderLayout
50889  * @extends Roo.LayoutManager
50890  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50891  * please see: <br><br>
50892  * <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>
50893  * <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>
50894  * Example:
50895  <pre><code>
50896  var layout = new Roo.BorderLayout(document.body, {
50897     north: {
50898         initialSize: 25,
50899         titlebar: false
50900     },
50901     west: {
50902         split:true,
50903         initialSize: 200,
50904         minSize: 175,
50905         maxSize: 400,
50906         titlebar: true,
50907         collapsible: true
50908     },
50909     east: {
50910         split:true,
50911         initialSize: 202,
50912         minSize: 175,
50913         maxSize: 400,
50914         titlebar: true,
50915         collapsible: true
50916     },
50917     south: {
50918         split:true,
50919         initialSize: 100,
50920         minSize: 100,
50921         maxSize: 200,
50922         titlebar: true,
50923         collapsible: true
50924     },
50925     center: {
50926         titlebar: true,
50927         autoScroll:true,
50928         resizeTabs: true,
50929         minTabWidth: 50,
50930         preferredTabWidth: 150
50931     }
50932 });
50933
50934 // shorthand
50935 var CP = Roo.ContentPanel;
50936
50937 layout.beginUpdate();
50938 layout.add("north", new CP("north", "North"));
50939 layout.add("south", new CP("south", {title: "South", closable: true}));
50940 layout.add("west", new CP("west", {title: "West"}));
50941 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50942 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50943 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50944 layout.getRegion("center").showPanel("center1");
50945 layout.endUpdate();
50946 </code></pre>
50947
50948 <b>The container the layout is rendered into can be either the body element or any other element.
50949 If it is not the body element, the container needs to either be an absolute positioned element,
50950 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50951 the container size if it is not the body element.</b>
50952
50953 * @constructor
50954 * Create a new BorderLayout
50955 * @param {String/HTMLElement/Element} container The container this layout is bound to
50956 * @param {Object} config Configuration options
50957  */
50958 Roo.BorderLayout = function(container, config){
50959     config = config || {};
50960     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50961     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50962     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50963         var target = this.factory.validRegions[i];
50964         if(config[target]){
50965             this.addRegion(target, config[target]);
50966         }
50967     }
50968 };
50969
50970 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50971     /**
50972      * Creates and adds a new region if it doesn't already exist.
50973      * @param {String} target The target region key (north, south, east, west or center).
50974      * @param {Object} config The regions config object
50975      * @return {BorderLayoutRegion} The new region
50976      */
50977     addRegion : function(target, config){
50978         if(!this.regions[target]){
50979             var r = this.factory.create(target, this, config);
50980             this.bindRegion(target, r);
50981         }
50982         return this.regions[target];
50983     },
50984
50985     // private (kinda)
50986     bindRegion : function(name, r){
50987         this.regions[name] = r;
50988         r.on("visibilitychange", this.layout, this);
50989         r.on("paneladded", this.layout, this);
50990         r.on("panelremoved", this.layout, this);
50991         r.on("invalidated", this.layout, this);
50992         r.on("resized", this.onRegionResized, this);
50993         r.on("collapsed", this.onRegionCollapsed, this);
50994         r.on("expanded", this.onRegionExpanded, this);
50995     },
50996
50997     /**
50998      * Performs a layout update.
50999      */
51000     layout : function(){
51001         if(this.updating) {
51002             return;
51003         }
51004         var size = this.getViewSize();
51005         var w = size.width;
51006         var h = size.height;
51007         var centerW = w;
51008         var centerH = h;
51009         var centerY = 0;
51010         var centerX = 0;
51011         //var x = 0, y = 0;
51012
51013         var rs = this.regions;
51014         var north = rs["north"];
51015         var south = rs["south"]; 
51016         var west = rs["west"];
51017         var east = rs["east"];
51018         var center = rs["center"];
51019         //if(this.hideOnLayout){ // not supported anymore
51020             //c.el.setStyle("display", "none");
51021         //}
51022         if(north && north.isVisible()){
51023             var b = north.getBox();
51024             var m = north.getMargins();
51025             b.width = w - (m.left+m.right);
51026             b.x = m.left;
51027             b.y = m.top;
51028             centerY = b.height + b.y + m.bottom;
51029             centerH -= centerY;
51030             north.updateBox(this.safeBox(b));
51031         }
51032         if(south && south.isVisible()){
51033             var b = south.getBox();
51034             var m = south.getMargins();
51035             b.width = w - (m.left+m.right);
51036             b.x = m.left;
51037             var totalHeight = (b.height + m.top + m.bottom);
51038             b.y = h - totalHeight + m.top;
51039             centerH -= totalHeight;
51040             south.updateBox(this.safeBox(b));
51041         }
51042         if(west && west.isVisible()){
51043             var b = west.getBox();
51044             var m = west.getMargins();
51045             b.height = centerH - (m.top+m.bottom);
51046             b.x = m.left;
51047             b.y = centerY + m.top;
51048             var totalWidth = (b.width + m.left + m.right);
51049             centerX += totalWidth;
51050             centerW -= totalWidth;
51051             west.updateBox(this.safeBox(b));
51052         }
51053         if(east && east.isVisible()){
51054             var b = east.getBox();
51055             var m = east.getMargins();
51056             b.height = centerH - (m.top+m.bottom);
51057             var totalWidth = (b.width + m.left + m.right);
51058             b.x = w - totalWidth + m.left;
51059             b.y = centerY + m.top;
51060             centerW -= totalWidth;
51061             east.updateBox(this.safeBox(b));
51062         }
51063         if(center){
51064             var m = center.getMargins();
51065             var centerBox = {
51066                 x: centerX + m.left,
51067                 y: centerY + m.top,
51068                 width: centerW - (m.left+m.right),
51069                 height: centerH - (m.top+m.bottom)
51070             };
51071             //if(this.hideOnLayout){
51072                 //center.el.setStyle("display", "block");
51073             //}
51074             center.updateBox(this.safeBox(centerBox));
51075         }
51076         this.el.repaint();
51077         this.fireEvent("layout", this);
51078     },
51079
51080     // private
51081     safeBox : function(box){
51082         box.width = Math.max(0, box.width);
51083         box.height = Math.max(0, box.height);
51084         return box;
51085     },
51086
51087     /**
51088      * Adds a ContentPanel (or subclass) to this layout.
51089      * @param {String} target The target region key (north, south, east, west or center).
51090      * @param {Roo.ContentPanel} panel The panel to add
51091      * @return {Roo.ContentPanel} The added panel
51092      */
51093     add : function(target, panel){
51094          
51095         target = target.toLowerCase();
51096         return this.regions[target].add(panel);
51097     },
51098
51099     /**
51100      * Remove a ContentPanel (or subclass) to this layout.
51101      * @param {String} target The target region key (north, south, east, west or center).
51102      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51103      * @return {Roo.ContentPanel} The removed panel
51104      */
51105     remove : function(target, panel){
51106         target = target.toLowerCase();
51107         return this.regions[target].remove(panel);
51108     },
51109
51110     /**
51111      * Searches all regions for a panel with the specified id
51112      * @param {String} panelId
51113      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51114      */
51115     findPanel : function(panelId){
51116         var rs = this.regions;
51117         for(var target in rs){
51118             if(typeof rs[target] != "function"){
51119                 var p = rs[target].getPanel(panelId);
51120                 if(p){
51121                     return p;
51122                 }
51123             }
51124         }
51125         return null;
51126     },
51127
51128     /**
51129      * Searches all regions for a panel with the specified id and activates (shows) it.
51130      * @param {String/ContentPanel} panelId The panels id or the panel itself
51131      * @return {Roo.ContentPanel} The shown panel or null
51132      */
51133     showPanel : function(panelId) {
51134       var rs = this.regions;
51135       for(var target in rs){
51136          var r = rs[target];
51137          if(typeof r != "function"){
51138             if(r.hasPanel(panelId)){
51139                return r.showPanel(panelId);
51140             }
51141          }
51142       }
51143       return null;
51144    },
51145
51146    /**
51147      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51148      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51149      */
51150     restoreState : function(provider){
51151         if(!provider){
51152             provider = Roo.state.Manager;
51153         }
51154         var sm = new Roo.LayoutStateManager();
51155         sm.init(this, provider);
51156     },
51157
51158     /**
51159      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51160      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51161      * a valid ContentPanel config object.  Example:
51162      * <pre><code>
51163 // Create the main layout
51164 var layout = new Roo.BorderLayout('main-ct', {
51165     west: {
51166         split:true,
51167         minSize: 175,
51168         titlebar: true
51169     },
51170     center: {
51171         title:'Components'
51172     }
51173 }, 'main-ct');
51174
51175 // Create and add multiple ContentPanels at once via configs
51176 layout.batchAdd({
51177    west: {
51178        id: 'source-files',
51179        autoCreate:true,
51180        title:'Ext Source Files',
51181        autoScroll:true,
51182        fitToFrame:true
51183    },
51184    center : {
51185        el: cview,
51186        autoScroll:true,
51187        fitToFrame:true,
51188        toolbar: tb,
51189        resizeEl:'cbody'
51190    }
51191 });
51192 </code></pre>
51193      * @param {Object} regions An object containing ContentPanel configs by region name
51194      */
51195     batchAdd : function(regions){
51196         this.beginUpdate();
51197         for(var rname in regions){
51198             var lr = this.regions[rname];
51199             if(lr){
51200                 this.addTypedPanels(lr, regions[rname]);
51201             }
51202         }
51203         this.endUpdate();
51204     },
51205
51206     // private
51207     addTypedPanels : function(lr, ps){
51208         if(typeof ps == 'string'){
51209             lr.add(new Roo.ContentPanel(ps));
51210         }
51211         else if(ps instanceof Array){
51212             for(var i =0, len = ps.length; i < len; i++){
51213                 this.addTypedPanels(lr, ps[i]);
51214             }
51215         }
51216         else if(!ps.events){ // raw config?
51217             var el = ps.el;
51218             delete ps.el; // prevent conflict
51219             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51220         }
51221         else {  // panel object assumed!
51222             lr.add(ps);
51223         }
51224     },
51225     /**
51226      * Adds a xtype elements to the layout.
51227      * <pre><code>
51228
51229 layout.addxtype({
51230        xtype : 'ContentPanel',
51231        region: 'west',
51232        items: [ .... ]
51233    }
51234 );
51235
51236 layout.addxtype({
51237         xtype : 'NestedLayoutPanel',
51238         region: 'west',
51239         layout: {
51240            center: { },
51241            west: { }   
51242         },
51243         items : [ ... list of content panels or nested layout panels.. ]
51244    }
51245 );
51246 </code></pre>
51247      * @param {Object} cfg Xtype definition of item to add.
51248      */
51249     addxtype : function(cfg)
51250     {
51251         // basically accepts a pannel...
51252         // can accept a layout region..!?!?
51253         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51254         
51255         if (!cfg.xtype.match(/Panel$/)) {
51256             return false;
51257         }
51258         var ret = false;
51259         
51260         if (typeof(cfg.region) == 'undefined') {
51261             Roo.log("Failed to add Panel, region was not set");
51262             Roo.log(cfg);
51263             return false;
51264         }
51265         var region = cfg.region;
51266         delete cfg.region;
51267         
51268           
51269         var xitems = [];
51270         if (cfg.items) {
51271             xitems = cfg.items;
51272             delete cfg.items;
51273         }
51274         var nb = false;
51275         
51276         switch(cfg.xtype) 
51277         {
51278             case 'ContentPanel':  // ContentPanel (el, cfg)
51279             case 'ScrollPanel':  // ContentPanel (el, cfg)
51280             case 'ViewPanel': 
51281                 if(cfg.autoCreate) {
51282                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51283                 } else {
51284                     var el = this.el.createChild();
51285                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51286                 }
51287                 
51288                 this.add(region, ret);
51289                 break;
51290             
51291             
51292             case 'TreePanel': // our new panel!
51293                 cfg.el = this.el.createChild();
51294                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51295                 this.add(region, ret);
51296                 break;
51297             
51298             case 'NestedLayoutPanel': 
51299                 // create a new Layout (which is  a Border Layout...
51300                 var el = this.el.createChild();
51301                 var clayout = cfg.layout;
51302                 delete cfg.layout;
51303                 clayout.items   = clayout.items  || [];
51304                 // replace this exitems with the clayout ones..
51305                 xitems = clayout.items;
51306                  
51307                 
51308                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51309                     cfg.background = false;
51310                 }
51311                 var layout = new Roo.BorderLayout(el, clayout);
51312                 
51313                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51314                 //console.log('adding nested layout panel '  + cfg.toSource());
51315                 this.add(region, ret);
51316                 nb = {}; /// find first...
51317                 break;
51318                 
51319             case 'GridPanel': 
51320             
51321                 // needs grid and region
51322                 
51323                 //var el = this.getRegion(region).el.createChild();
51324                 var el = this.el.createChild();
51325                 // create the grid first...
51326                 
51327                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51328                 delete cfg.grid;
51329                 if (region == 'center' && this.active ) {
51330                     cfg.background = false;
51331                 }
51332                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51333                 
51334                 this.add(region, ret);
51335                 if (cfg.background) {
51336                     ret.on('activate', function(gp) {
51337                         if (!gp.grid.rendered) {
51338                             gp.grid.render();
51339                         }
51340                     });
51341                 } else {
51342                     grid.render();
51343                 }
51344                 break;
51345            
51346            
51347            
51348                 
51349                 
51350                 
51351             default:
51352                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51353                     
51354                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51355                     this.add(region, ret);
51356                 } else {
51357                 
51358                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51359                     return null;
51360                 }
51361                 
51362              // GridPanel (grid, cfg)
51363             
51364         }
51365         this.beginUpdate();
51366         // add children..
51367         var region = '';
51368         var abn = {};
51369         Roo.each(xitems, function(i)  {
51370             region = nb && i.region ? i.region : false;
51371             
51372             var add = ret.addxtype(i);
51373            
51374             if (region) {
51375                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51376                 if (!i.background) {
51377                     abn[region] = nb[region] ;
51378                 }
51379             }
51380             
51381         });
51382         this.endUpdate();
51383
51384         // make the last non-background panel active..
51385         //if (nb) { Roo.log(abn); }
51386         if (nb) {
51387             
51388             for(var r in abn) {
51389                 region = this.getRegion(r);
51390                 if (region) {
51391                     // tried using nb[r], but it does not work..
51392                      
51393                     region.showPanel(abn[r]);
51394                    
51395                 }
51396             }
51397         }
51398         return ret;
51399         
51400     }
51401 });
51402
51403 /**
51404  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51405  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51406  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51407  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51408  * <pre><code>
51409 // shorthand
51410 var CP = Roo.ContentPanel;
51411
51412 var layout = Roo.BorderLayout.create({
51413     north: {
51414         initialSize: 25,
51415         titlebar: false,
51416         panels: [new CP("north", "North")]
51417     },
51418     west: {
51419         split:true,
51420         initialSize: 200,
51421         minSize: 175,
51422         maxSize: 400,
51423         titlebar: true,
51424         collapsible: true,
51425         panels: [new CP("west", {title: "West"})]
51426     },
51427     east: {
51428         split:true,
51429         initialSize: 202,
51430         minSize: 175,
51431         maxSize: 400,
51432         titlebar: true,
51433         collapsible: true,
51434         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51435     },
51436     south: {
51437         split:true,
51438         initialSize: 100,
51439         minSize: 100,
51440         maxSize: 200,
51441         titlebar: true,
51442         collapsible: true,
51443         panels: [new CP("south", {title: "South", closable: true})]
51444     },
51445     center: {
51446         titlebar: true,
51447         autoScroll:true,
51448         resizeTabs: true,
51449         minTabWidth: 50,
51450         preferredTabWidth: 150,
51451         panels: [
51452             new CP("center1", {title: "Close Me", closable: true}),
51453             new CP("center2", {title: "Center Panel", closable: false})
51454         ]
51455     }
51456 }, document.body);
51457
51458 layout.getRegion("center").showPanel("center1");
51459 </code></pre>
51460  * @param config
51461  * @param targetEl
51462  */
51463 Roo.BorderLayout.create = function(config, targetEl){
51464     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51465     layout.beginUpdate();
51466     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51467     for(var j = 0, jlen = regions.length; j < jlen; j++){
51468         var lr = regions[j];
51469         if(layout.regions[lr] && config[lr].panels){
51470             var r = layout.regions[lr];
51471             var ps = config[lr].panels;
51472             layout.addTypedPanels(r, ps);
51473         }
51474     }
51475     layout.endUpdate();
51476     return layout;
51477 };
51478
51479 // private
51480 Roo.BorderLayout.RegionFactory = {
51481     // private
51482     validRegions : ["north","south","east","west","center"],
51483
51484     // private
51485     create : function(target, mgr, config){
51486         target = target.toLowerCase();
51487         if(config.lightweight || config.basic){
51488             return new Roo.BasicLayoutRegion(mgr, config, target);
51489         }
51490         switch(target){
51491             case "north":
51492                 return new Roo.NorthLayoutRegion(mgr, config);
51493             case "south":
51494                 return new Roo.SouthLayoutRegion(mgr, config);
51495             case "east":
51496                 return new Roo.EastLayoutRegion(mgr, config);
51497             case "west":
51498                 return new Roo.WestLayoutRegion(mgr, config);
51499             case "center":
51500                 return new Roo.CenterLayoutRegion(mgr, config);
51501         }
51502         throw 'Layout region "'+target+'" not supported.';
51503     }
51504 };/*
51505  * Based on:
51506  * Ext JS Library 1.1.1
51507  * Copyright(c) 2006-2007, Ext JS, LLC.
51508  *
51509  * Originally Released Under LGPL - original licence link has changed is not relivant.
51510  *
51511  * Fork - LGPL
51512  * <script type="text/javascript">
51513  */
51514  
51515 /**
51516  * @class Roo.BasicLayoutRegion
51517  * @extends Roo.util.Observable
51518  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51519  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51520  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51521  */
51522 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51523     this.mgr = mgr;
51524     this.position  = pos;
51525     this.events = {
51526         /**
51527          * @scope Roo.BasicLayoutRegion
51528          */
51529         
51530         /**
51531          * @event beforeremove
51532          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51533          * @param {Roo.LayoutRegion} this
51534          * @param {Roo.ContentPanel} panel The panel
51535          * @param {Object} e The cancel event object
51536          */
51537         "beforeremove" : true,
51538         /**
51539          * @event invalidated
51540          * Fires when the layout for this region is changed.
51541          * @param {Roo.LayoutRegion} this
51542          */
51543         "invalidated" : true,
51544         /**
51545          * @event visibilitychange
51546          * Fires when this region is shown or hidden 
51547          * @param {Roo.LayoutRegion} this
51548          * @param {Boolean} visibility true or false
51549          */
51550         "visibilitychange" : true,
51551         /**
51552          * @event paneladded
51553          * Fires when a panel is added. 
51554          * @param {Roo.LayoutRegion} this
51555          * @param {Roo.ContentPanel} panel The panel
51556          */
51557         "paneladded" : true,
51558         /**
51559          * @event panelremoved
51560          * Fires when a panel is removed. 
51561          * @param {Roo.LayoutRegion} this
51562          * @param {Roo.ContentPanel} panel The panel
51563          */
51564         "panelremoved" : true,
51565         /**
51566          * @event beforecollapse
51567          * Fires when this region before collapse.
51568          * @param {Roo.LayoutRegion} this
51569          */
51570         "beforecollapse" : true,
51571         /**
51572          * @event collapsed
51573          * Fires when this region is collapsed.
51574          * @param {Roo.LayoutRegion} this
51575          */
51576         "collapsed" : true,
51577         /**
51578          * @event expanded
51579          * Fires when this region is expanded.
51580          * @param {Roo.LayoutRegion} this
51581          */
51582         "expanded" : true,
51583         /**
51584          * @event slideshow
51585          * Fires when this region is slid into view.
51586          * @param {Roo.LayoutRegion} this
51587          */
51588         "slideshow" : true,
51589         /**
51590          * @event slidehide
51591          * Fires when this region slides out of view. 
51592          * @param {Roo.LayoutRegion} this
51593          */
51594         "slidehide" : true,
51595         /**
51596          * @event panelactivated
51597          * Fires when a panel is activated. 
51598          * @param {Roo.LayoutRegion} this
51599          * @param {Roo.ContentPanel} panel The activated panel
51600          */
51601         "panelactivated" : true,
51602         /**
51603          * @event resized
51604          * Fires when the user resizes this region. 
51605          * @param {Roo.LayoutRegion} this
51606          * @param {Number} newSize The new size (width for east/west, height for north/south)
51607          */
51608         "resized" : true
51609     };
51610     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51611     this.panels = new Roo.util.MixedCollection();
51612     this.panels.getKey = this.getPanelId.createDelegate(this);
51613     this.box = null;
51614     this.activePanel = null;
51615     // ensure listeners are added...
51616     
51617     if (config.listeners || config.events) {
51618         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51619             listeners : config.listeners || {},
51620             events : config.events || {}
51621         });
51622     }
51623     
51624     if(skipConfig !== true){
51625         this.applyConfig(config);
51626     }
51627 };
51628
51629 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51630     getPanelId : function(p){
51631         return p.getId();
51632     },
51633     
51634     applyConfig : function(config){
51635         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51636         this.config = config;
51637         
51638     },
51639     
51640     /**
51641      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51642      * the width, for horizontal (north, south) the height.
51643      * @param {Number} newSize The new width or height
51644      */
51645     resizeTo : function(newSize){
51646         var el = this.el ? this.el :
51647                  (this.activePanel ? this.activePanel.getEl() : null);
51648         if(el){
51649             switch(this.position){
51650                 case "east":
51651                 case "west":
51652                     el.setWidth(newSize);
51653                     this.fireEvent("resized", this, newSize);
51654                 break;
51655                 case "north":
51656                 case "south":
51657                     el.setHeight(newSize);
51658                     this.fireEvent("resized", this, newSize);
51659                 break;                
51660             }
51661         }
51662     },
51663     
51664     getBox : function(){
51665         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51666     },
51667     
51668     getMargins : function(){
51669         return this.margins;
51670     },
51671     
51672     updateBox : function(box){
51673         this.box = box;
51674         var el = this.activePanel.getEl();
51675         el.dom.style.left = box.x + "px";
51676         el.dom.style.top = box.y + "px";
51677         this.activePanel.setSize(box.width, box.height);
51678     },
51679     
51680     /**
51681      * Returns the container element for this region.
51682      * @return {Roo.Element}
51683      */
51684     getEl : function(){
51685         return this.activePanel;
51686     },
51687     
51688     /**
51689      * Returns true if this region is currently visible.
51690      * @return {Boolean}
51691      */
51692     isVisible : function(){
51693         return this.activePanel ? true : false;
51694     },
51695     
51696     setActivePanel : function(panel){
51697         panel = this.getPanel(panel);
51698         if(this.activePanel && this.activePanel != panel){
51699             this.activePanel.setActiveState(false);
51700             this.activePanel.getEl().setLeftTop(-10000,-10000);
51701         }
51702         this.activePanel = panel;
51703         panel.setActiveState(true);
51704         if(this.box){
51705             panel.setSize(this.box.width, this.box.height);
51706         }
51707         this.fireEvent("panelactivated", this, panel);
51708         this.fireEvent("invalidated");
51709     },
51710     
51711     /**
51712      * Show the specified panel.
51713      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51714      * @return {Roo.ContentPanel} The shown panel or null
51715      */
51716     showPanel : function(panel){
51717         if(panel = this.getPanel(panel)){
51718             this.setActivePanel(panel);
51719         }
51720         return panel;
51721     },
51722     
51723     /**
51724      * Get the active panel for this region.
51725      * @return {Roo.ContentPanel} The active panel or null
51726      */
51727     getActivePanel : function(){
51728         return this.activePanel;
51729     },
51730     
51731     /**
51732      * Add the passed ContentPanel(s)
51733      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51734      * @return {Roo.ContentPanel} The panel added (if only one was added)
51735      */
51736     add : function(panel){
51737         if(arguments.length > 1){
51738             for(var i = 0, len = arguments.length; i < len; i++) {
51739                 this.add(arguments[i]);
51740             }
51741             return null;
51742         }
51743         if(this.hasPanel(panel)){
51744             this.showPanel(panel);
51745             return panel;
51746         }
51747         var el = panel.getEl();
51748         if(el.dom.parentNode != this.mgr.el.dom){
51749             this.mgr.el.dom.appendChild(el.dom);
51750         }
51751         if(panel.setRegion){
51752             panel.setRegion(this);
51753         }
51754         this.panels.add(panel);
51755         el.setStyle("position", "absolute");
51756         if(!panel.background){
51757             this.setActivePanel(panel);
51758             if(this.config.initialSize && this.panels.getCount()==1){
51759                 this.resizeTo(this.config.initialSize);
51760             }
51761         }
51762         this.fireEvent("paneladded", this, panel);
51763         return panel;
51764     },
51765     
51766     /**
51767      * Returns true if the panel is in this region.
51768      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51769      * @return {Boolean}
51770      */
51771     hasPanel : function(panel){
51772         if(typeof panel == "object"){ // must be panel obj
51773             panel = panel.getId();
51774         }
51775         return this.getPanel(panel) ? true : false;
51776     },
51777     
51778     /**
51779      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51780      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51781      * @param {Boolean} preservePanel Overrides the config preservePanel option
51782      * @return {Roo.ContentPanel} The panel that was removed
51783      */
51784     remove : function(panel, preservePanel){
51785         panel = this.getPanel(panel);
51786         if(!panel){
51787             return null;
51788         }
51789         var e = {};
51790         this.fireEvent("beforeremove", this, panel, e);
51791         if(e.cancel === true){
51792             return null;
51793         }
51794         var panelId = panel.getId();
51795         this.panels.removeKey(panelId);
51796         return panel;
51797     },
51798     
51799     /**
51800      * Returns the panel specified or null if it's not in this region.
51801      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51802      * @return {Roo.ContentPanel}
51803      */
51804     getPanel : function(id){
51805         if(typeof id == "object"){ // must be panel obj
51806             return id;
51807         }
51808         return this.panels.get(id);
51809     },
51810     
51811     /**
51812      * Returns this regions position (north/south/east/west/center).
51813      * @return {String} 
51814      */
51815     getPosition: function(){
51816         return this.position;    
51817     }
51818 });/*
51819  * Based on:
51820  * Ext JS Library 1.1.1
51821  * Copyright(c) 2006-2007, Ext JS, LLC.
51822  *
51823  * Originally Released Under LGPL - original licence link has changed is not relivant.
51824  *
51825  * Fork - LGPL
51826  * <script type="text/javascript">
51827  */
51828  
51829 /**
51830  * @class Roo.LayoutRegion
51831  * @extends Roo.BasicLayoutRegion
51832  * This class represents a region in a layout manager.
51833  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51834  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51835  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51836  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51837  * @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})
51838  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51839  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51840  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51841  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51842  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51843  * @cfg {String}    title           The title for the region (overrides panel titles)
51844  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51845  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51846  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51847  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51848  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51849  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51850  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51851  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51852  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51853  * @cfg {Boolean}   showPin         True to show a pin button
51854  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51855  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51856  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51857  * @cfg {Number}    width           For East/West panels
51858  * @cfg {Number}    height          For North/South panels
51859  * @cfg {Boolean}   split           To show the splitter
51860  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51861  */
51862 Roo.LayoutRegion = function(mgr, config, pos){
51863     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51864     var dh = Roo.DomHelper;
51865     /** This region's container element 
51866     * @type Roo.Element */
51867     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51868     /** This region's title element 
51869     * @type Roo.Element */
51870
51871     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51872         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51873         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51874     ]}, true);
51875     this.titleEl.enableDisplayMode();
51876     /** This region's title text element 
51877     * @type HTMLElement */
51878     this.titleTextEl = this.titleEl.dom.firstChild;
51879     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51880     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51881     this.closeBtn.enableDisplayMode();
51882     this.closeBtn.on("click", this.closeClicked, this);
51883     this.closeBtn.hide();
51884
51885     this.createBody(config);
51886     this.visible = true;
51887     this.collapsed = false;
51888
51889     if(config.hideWhenEmpty){
51890         this.hide();
51891         this.on("paneladded", this.validateVisibility, this);
51892         this.on("panelremoved", this.validateVisibility, this);
51893     }
51894     this.applyConfig(config);
51895 };
51896
51897 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51898
51899     createBody : function(){
51900         /** This region's body element 
51901         * @type Roo.Element */
51902         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51903     },
51904
51905     applyConfig : function(c){
51906         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51907             var dh = Roo.DomHelper;
51908             if(c.titlebar !== false){
51909                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51910                 this.collapseBtn.on("click", this.collapse, this);
51911                 this.collapseBtn.enableDisplayMode();
51912
51913                 if(c.showPin === true || this.showPin){
51914                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51915                     this.stickBtn.enableDisplayMode();
51916                     this.stickBtn.on("click", this.expand, this);
51917                     this.stickBtn.hide();
51918                 }
51919             }
51920             /** This region's collapsed element
51921             * @type Roo.Element */
51922             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51923                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51924             ]}, true);
51925             if(c.floatable !== false){
51926                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51927                this.collapsedEl.on("click", this.collapseClick, this);
51928             }
51929
51930             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51931                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51932                    id: "message", unselectable: "on", style:{"float":"left"}});
51933                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51934              }
51935             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51936             this.expandBtn.on("click", this.expand, this);
51937         }
51938         if(this.collapseBtn){
51939             this.collapseBtn.setVisible(c.collapsible == true);
51940         }
51941         this.cmargins = c.cmargins || this.cmargins ||
51942                          (this.position == "west" || this.position == "east" ?
51943                              {top: 0, left: 2, right:2, bottom: 0} :
51944                              {top: 2, left: 0, right:0, bottom: 2});
51945         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51946         this.bottomTabs = c.tabPosition != "top";
51947         this.autoScroll = c.autoScroll || false;
51948         if(this.autoScroll){
51949             this.bodyEl.setStyle("overflow", "auto");
51950         }else{
51951             this.bodyEl.setStyle("overflow", "hidden");
51952         }
51953         //if(c.titlebar !== false){
51954             if((!c.titlebar && !c.title) || c.titlebar === false){
51955                 this.titleEl.hide();
51956             }else{
51957                 this.titleEl.show();
51958                 if(c.title){
51959                     this.titleTextEl.innerHTML = c.title;
51960                 }
51961             }
51962         //}
51963         this.duration = c.duration || .30;
51964         this.slideDuration = c.slideDuration || .45;
51965         this.config = c;
51966         if(c.collapsed){
51967             this.collapse(true);
51968         }
51969         if(c.hidden){
51970             this.hide();
51971         }
51972     },
51973     /**
51974      * Returns true if this region is currently visible.
51975      * @return {Boolean}
51976      */
51977     isVisible : function(){
51978         return this.visible;
51979     },
51980
51981     /**
51982      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51983      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51984      */
51985     setCollapsedTitle : function(title){
51986         title = title || "&#160;";
51987         if(this.collapsedTitleTextEl){
51988             this.collapsedTitleTextEl.innerHTML = title;
51989         }
51990     },
51991
51992     getBox : function(){
51993         var b;
51994         if(!this.collapsed){
51995             b = this.el.getBox(false, true);
51996         }else{
51997             b = this.collapsedEl.getBox(false, true);
51998         }
51999         return b;
52000     },
52001
52002     getMargins : function(){
52003         return this.collapsed ? this.cmargins : this.margins;
52004     },
52005
52006     highlight : function(){
52007         this.el.addClass("x-layout-panel-dragover");
52008     },
52009
52010     unhighlight : function(){
52011         this.el.removeClass("x-layout-panel-dragover");
52012     },
52013
52014     updateBox : function(box){
52015         this.box = box;
52016         if(!this.collapsed){
52017             this.el.dom.style.left = box.x + "px";
52018             this.el.dom.style.top = box.y + "px";
52019             this.updateBody(box.width, box.height);
52020         }else{
52021             this.collapsedEl.dom.style.left = box.x + "px";
52022             this.collapsedEl.dom.style.top = box.y + "px";
52023             this.collapsedEl.setSize(box.width, box.height);
52024         }
52025         if(this.tabs){
52026             this.tabs.autoSizeTabs();
52027         }
52028     },
52029
52030     updateBody : function(w, h){
52031         if(w !== null){
52032             this.el.setWidth(w);
52033             w -= this.el.getBorderWidth("rl");
52034             if(this.config.adjustments){
52035                 w += this.config.adjustments[0];
52036             }
52037         }
52038         if(h !== null){
52039             this.el.setHeight(h);
52040             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52041             h -= this.el.getBorderWidth("tb");
52042             if(this.config.adjustments){
52043                 h += this.config.adjustments[1];
52044             }
52045             this.bodyEl.setHeight(h);
52046             if(this.tabs){
52047                 h = this.tabs.syncHeight(h);
52048             }
52049         }
52050         if(this.panelSize){
52051             w = w !== null ? w : this.panelSize.width;
52052             h = h !== null ? h : this.panelSize.height;
52053         }
52054         if(this.activePanel){
52055             var el = this.activePanel.getEl();
52056             w = w !== null ? w : el.getWidth();
52057             h = h !== null ? h : el.getHeight();
52058             this.panelSize = {width: w, height: h};
52059             this.activePanel.setSize(w, h);
52060         }
52061         if(Roo.isIE && this.tabs){
52062             this.tabs.el.repaint();
52063         }
52064     },
52065
52066     /**
52067      * Returns the container element for this region.
52068      * @return {Roo.Element}
52069      */
52070     getEl : function(){
52071         return this.el;
52072     },
52073
52074     /**
52075      * Hides this region.
52076      */
52077     hide : function(){
52078         if(!this.collapsed){
52079             this.el.dom.style.left = "-2000px";
52080             this.el.hide();
52081         }else{
52082             this.collapsedEl.dom.style.left = "-2000px";
52083             this.collapsedEl.hide();
52084         }
52085         this.visible = false;
52086         this.fireEvent("visibilitychange", this, false);
52087     },
52088
52089     /**
52090      * Shows this region if it was previously hidden.
52091      */
52092     show : function(){
52093         if(!this.collapsed){
52094             this.el.show();
52095         }else{
52096             this.collapsedEl.show();
52097         }
52098         this.visible = true;
52099         this.fireEvent("visibilitychange", this, true);
52100     },
52101
52102     closeClicked : function(){
52103         if(this.activePanel){
52104             this.remove(this.activePanel);
52105         }
52106     },
52107
52108     collapseClick : function(e){
52109         if(this.isSlid){
52110            e.stopPropagation();
52111            this.slideIn();
52112         }else{
52113            e.stopPropagation();
52114            this.slideOut();
52115         }
52116     },
52117
52118     /**
52119      * Collapses this region.
52120      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52121      */
52122     collapse : function(skipAnim, skipCheck = false){
52123         if(this.collapsed) {
52124             return;
52125         }
52126         
52127         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52128             
52129             this.collapsed = true;
52130             if(this.split){
52131                 this.split.el.hide();
52132             }
52133             if(this.config.animate && skipAnim !== true){
52134                 this.fireEvent("invalidated", this);
52135                 this.animateCollapse();
52136             }else{
52137                 this.el.setLocation(-20000,-20000);
52138                 this.el.hide();
52139                 this.collapsedEl.show();
52140                 this.fireEvent("collapsed", this);
52141                 this.fireEvent("invalidated", this);
52142             }
52143         }
52144         
52145     },
52146
52147     animateCollapse : function(){
52148         // overridden
52149     },
52150
52151     /**
52152      * Expands this region if it was previously collapsed.
52153      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52154      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52155      */
52156     expand : function(e, skipAnim){
52157         if(e) {
52158             e.stopPropagation();
52159         }
52160         if(!this.collapsed || this.el.hasActiveFx()) {
52161             return;
52162         }
52163         if(this.isSlid){
52164             this.afterSlideIn();
52165             skipAnim = true;
52166         }
52167         this.collapsed = false;
52168         if(this.config.animate && skipAnim !== true){
52169             this.animateExpand();
52170         }else{
52171             this.el.show();
52172             if(this.split){
52173                 this.split.el.show();
52174             }
52175             this.collapsedEl.setLocation(-2000,-2000);
52176             this.collapsedEl.hide();
52177             this.fireEvent("invalidated", this);
52178             this.fireEvent("expanded", this);
52179         }
52180     },
52181
52182     animateExpand : function(){
52183         // overridden
52184     },
52185
52186     initTabs : function()
52187     {
52188         this.bodyEl.setStyle("overflow", "hidden");
52189         var ts = new Roo.TabPanel(
52190                 this.bodyEl.dom,
52191                 {
52192                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52193                     disableTooltips: this.config.disableTabTips,
52194                     toolbar : this.config.toolbar
52195                 }
52196         );
52197         if(this.config.hideTabs){
52198             ts.stripWrap.setDisplayed(false);
52199         }
52200         this.tabs = ts;
52201         ts.resizeTabs = this.config.resizeTabs === true;
52202         ts.minTabWidth = this.config.minTabWidth || 40;
52203         ts.maxTabWidth = this.config.maxTabWidth || 250;
52204         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52205         ts.monitorResize = false;
52206         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52207         ts.bodyEl.addClass('x-layout-tabs-body');
52208         this.panels.each(this.initPanelAsTab, this);
52209     },
52210
52211     initPanelAsTab : function(panel){
52212         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52213                     this.config.closeOnTab && panel.isClosable());
52214         if(panel.tabTip !== undefined){
52215             ti.setTooltip(panel.tabTip);
52216         }
52217         ti.on("activate", function(){
52218               this.setActivePanel(panel);
52219         }, this);
52220         if(this.config.closeOnTab){
52221             ti.on("beforeclose", function(t, e){
52222                 e.cancel = true;
52223                 this.remove(panel);
52224             }, this);
52225         }
52226         return ti;
52227     },
52228
52229     updatePanelTitle : function(panel, title){
52230         if(this.activePanel == panel){
52231             this.updateTitle(title);
52232         }
52233         if(this.tabs){
52234             var ti = this.tabs.getTab(panel.getEl().id);
52235             ti.setText(title);
52236             if(panel.tabTip !== undefined){
52237                 ti.setTooltip(panel.tabTip);
52238             }
52239         }
52240     },
52241
52242     updateTitle : function(title){
52243         if(this.titleTextEl && !this.config.title){
52244             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52245         }
52246     },
52247
52248     setActivePanel : function(panel){
52249         panel = this.getPanel(panel);
52250         if(this.activePanel && this.activePanel != panel){
52251             this.activePanel.setActiveState(false);
52252         }
52253         this.activePanel = panel;
52254         panel.setActiveState(true);
52255         if(this.panelSize){
52256             panel.setSize(this.panelSize.width, this.panelSize.height);
52257         }
52258         if(this.closeBtn){
52259             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52260         }
52261         this.updateTitle(panel.getTitle());
52262         if(this.tabs){
52263             this.fireEvent("invalidated", this);
52264         }
52265         this.fireEvent("panelactivated", this, panel);
52266     },
52267
52268     /**
52269      * Shows the specified panel.
52270      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52271      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52272      */
52273     showPanel : function(panel)
52274     {
52275         panel = this.getPanel(panel);
52276         if(panel){
52277             if(this.tabs){
52278                 var tab = this.tabs.getTab(panel.getEl().id);
52279                 if(tab.isHidden()){
52280                     this.tabs.unhideTab(tab.id);
52281                 }
52282                 tab.activate();
52283             }else{
52284                 this.setActivePanel(panel);
52285             }
52286         }
52287         return panel;
52288     },
52289
52290     /**
52291      * Get the active panel for this region.
52292      * @return {Roo.ContentPanel} The active panel or null
52293      */
52294     getActivePanel : function(){
52295         return this.activePanel;
52296     },
52297
52298     validateVisibility : function(){
52299         if(this.panels.getCount() < 1){
52300             this.updateTitle("&#160;");
52301             this.closeBtn.hide();
52302             this.hide();
52303         }else{
52304             if(!this.isVisible()){
52305                 this.show();
52306             }
52307         }
52308     },
52309
52310     /**
52311      * Adds the passed ContentPanel(s) to this region.
52312      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52313      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52314      */
52315     add : function(panel){
52316         if(arguments.length > 1){
52317             for(var i = 0, len = arguments.length; i < len; i++) {
52318                 this.add(arguments[i]);
52319             }
52320             return null;
52321         }
52322         if(this.hasPanel(panel)){
52323             this.showPanel(panel);
52324             return panel;
52325         }
52326         panel.setRegion(this);
52327         this.panels.add(panel);
52328         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52329             this.bodyEl.dom.appendChild(panel.getEl().dom);
52330             if(panel.background !== true){
52331                 this.setActivePanel(panel);
52332             }
52333             this.fireEvent("paneladded", this, panel);
52334             return panel;
52335         }
52336         if(!this.tabs){
52337             this.initTabs();
52338         }else{
52339             this.initPanelAsTab(panel);
52340         }
52341         if(panel.background !== true){
52342             this.tabs.activate(panel.getEl().id);
52343         }
52344         this.fireEvent("paneladded", this, panel);
52345         return panel;
52346     },
52347
52348     /**
52349      * Hides the tab for the specified panel.
52350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52351      */
52352     hidePanel : function(panel){
52353         if(this.tabs && (panel = this.getPanel(panel))){
52354             this.tabs.hideTab(panel.getEl().id);
52355         }
52356     },
52357
52358     /**
52359      * Unhides the tab for a previously hidden panel.
52360      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52361      */
52362     unhidePanel : function(panel){
52363         if(this.tabs && (panel = this.getPanel(panel))){
52364             this.tabs.unhideTab(panel.getEl().id);
52365         }
52366     },
52367
52368     clearPanels : function(){
52369         while(this.panels.getCount() > 0){
52370              this.remove(this.panels.first());
52371         }
52372     },
52373
52374     /**
52375      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52376      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52377      * @param {Boolean} preservePanel Overrides the config preservePanel option
52378      * @return {Roo.ContentPanel} The panel that was removed
52379      */
52380     remove : function(panel, preservePanel){
52381         panel = this.getPanel(panel);
52382         if(!panel){
52383             return null;
52384         }
52385         var e = {};
52386         this.fireEvent("beforeremove", this, panel, e);
52387         if(e.cancel === true){
52388             return null;
52389         }
52390         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52391         var panelId = panel.getId();
52392         this.panels.removeKey(panelId);
52393         if(preservePanel){
52394             document.body.appendChild(panel.getEl().dom);
52395         }
52396         if(this.tabs){
52397             this.tabs.removeTab(panel.getEl().id);
52398         }else if (!preservePanel){
52399             this.bodyEl.dom.removeChild(panel.getEl().dom);
52400         }
52401         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52402             var p = this.panels.first();
52403             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52404             tempEl.appendChild(p.getEl().dom);
52405             this.bodyEl.update("");
52406             this.bodyEl.dom.appendChild(p.getEl().dom);
52407             tempEl = null;
52408             this.updateTitle(p.getTitle());
52409             this.tabs = null;
52410             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52411             this.setActivePanel(p);
52412         }
52413         panel.setRegion(null);
52414         if(this.activePanel == panel){
52415             this.activePanel = null;
52416         }
52417         if(this.config.autoDestroy !== false && preservePanel !== true){
52418             try{panel.destroy();}catch(e){}
52419         }
52420         this.fireEvent("panelremoved", this, panel);
52421         return panel;
52422     },
52423
52424     /**
52425      * Returns the TabPanel component used by this region
52426      * @return {Roo.TabPanel}
52427      */
52428     getTabs : function(){
52429         return this.tabs;
52430     },
52431
52432     createTool : function(parentEl, className){
52433         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52434             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52435         btn.addClassOnOver("x-layout-tools-button-over");
52436         return btn;
52437     }
52438 });/*
52439  * Based on:
52440  * Ext JS Library 1.1.1
52441  * Copyright(c) 2006-2007, Ext JS, LLC.
52442  *
52443  * Originally Released Under LGPL - original licence link has changed is not relivant.
52444  *
52445  * Fork - LGPL
52446  * <script type="text/javascript">
52447  */
52448  
52449
52450
52451 /**
52452  * @class Roo.SplitLayoutRegion
52453  * @extends Roo.LayoutRegion
52454  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52455  */
52456 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52457     this.cursor = cursor;
52458     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52459 };
52460
52461 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52462     splitTip : "Drag to resize.",
52463     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52464     useSplitTips : false,
52465
52466     applyConfig : function(config){
52467         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52468         if(config.split){
52469             if(!this.split){
52470                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52471                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52472                 /** The SplitBar for this region 
52473                 * @type Roo.SplitBar */
52474                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52475                 this.split.on("moved", this.onSplitMove, this);
52476                 this.split.useShim = config.useShim === true;
52477                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52478                 if(this.useSplitTips){
52479                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52480                 }
52481                 if(config.collapsible){
52482                     this.split.el.on("dblclick", this.collapse,  this);
52483                 }
52484             }
52485             if(typeof config.minSize != "undefined"){
52486                 this.split.minSize = config.minSize;
52487             }
52488             if(typeof config.maxSize != "undefined"){
52489                 this.split.maxSize = config.maxSize;
52490             }
52491             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52492                 this.hideSplitter();
52493             }
52494         }
52495     },
52496
52497     getHMaxSize : function(){
52498          var cmax = this.config.maxSize || 10000;
52499          var center = this.mgr.getRegion("center");
52500          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52501     },
52502
52503     getVMaxSize : function(){
52504          var cmax = this.config.maxSize || 10000;
52505          var center = this.mgr.getRegion("center");
52506          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52507     },
52508
52509     onSplitMove : function(split, newSize){
52510         this.fireEvent("resized", this, newSize);
52511     },
52512     
52513     /** 
52514      * Returns the {@link Roo.SplitBar} for this region.
52515      * @return {Roo.SplitBar}
52516      */
52517     getSplitBar : function(){
52518         return this.split;
52519     },
52520     
52521     hide : function(){
52522         this.hideSplitter();
52523         Roo.SplitLayoutRegion.superclass.hide.call(this);
52524     },
52525
52526     hideSplitter : function(){
52527         if(this.split){
52528             this.split.el.setLocation(-2000,-2000);
52529             this.split.el.hide();
52530         }
52531     },
52532
52533     show : function(){
52534         if(this.split){
52535             this.split.el.show();
52536         }
52537         Roo.SplitLayoutRegion.superclass.show.call(this);
52538     },
52539     
52540     beforeSlide: function(){
52541         if(Roo.isGecko){// firefox overflow auto bug workaround
52542             this.bodyEl.clip();
52543             if(this.tabs) {
52544                 this.tabs.bodyEl.clip();
52545             }
52546             if(this.activePanel){
52547                 this.activePanel.getEl().clip();
52548                 
52549                 if(this.activePanel.beforeSlide){
52550                     this.activePanel.beforeSlide();
52551                 }
52552             }
52553         }
52554     },
52555     
52556     afterSlide : function(){
52557         if(Roo.isGecko){// firefox overflow auto bug workaround
52558             this.bodyEl.unclip();
52559             if(this.tabs) {
52560                 this.tabs.bodyEl.unclip();
52561             }
52562             if(this.activePanel){
52563                 this.activePanel.getEl().unclip();
52564                 if(this.activePanel.afterSlide){
52565                     this.activePanel.afterSlide();
52566                 }
52567             }
52568         }
52569     },
52570
52571     initAutoHide : function(){
52572         if(this.autoHide !== false){
52573             if(!this.autoHideHd){
52574                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52575                 this.autoHideHd = {
52576                     "mouseout": function(e){
52577                         if(!e.within(this.el, true)){
52578                             st.delay(500);
52579                         }
52580                     },
52581                     "mouseover" : function(e){
52582                         st.cancel();
52583                     },
52584                     scope : this
52585                 };
52586             }
52587             this.el.on(this.autoHideHd);
52588         }
52589     },
52590
52591     clearAutoHide : function(){
52592         if(this.autoHide !== false){
52593             this.el.un("mouseout", this.autoHideHd.mouseout);
52594             this.el.un("mouseover", this.autoHideHd.mouseover);
52595         }
52596     },
52597
52598     clearMonitor : function(){
52599         Roo.get(document).un("click", this.slideInIf, this);
52600     },
52601
52602     // these names are backwards but not changed for compat
52603     slideOut : function(){
52604         if(this.isSlid || this.el.hasActiveFx()){
52605             return;
52606         }
52607         this.isSlid = true;
52608         if(this.collapseBtn){
52609             this.collapseBtn.hide();
52610         }
52611         this.closeBtnState = this.closeBtn.getStyle('display');
52612         this.closeBtn.hide();
52613         if(this.stickBtn){
52614             this.stickBtn.show();
52615         }
52616         this.el.show();
52617         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52618         this.beforeSlide();
52619         this.el.setStyle("z-index", 10001);
52620         this.el.slideIn(this.getSlideAnchor(), {
52621             callback: function(){
52622                 this.afterSlide();
52623                 this.initAutoHide();
52624                 Roo.get(document).on("click", this.slideInIf, this);
52625                 this.fireEvent("slideshow", this);
52626             },
52627             scope: this,
52628             block: true
52629         });
52630     },
52631
52632     afterSlideIn : function(){
52633         this.clearAutoHide();
52634         this.isSlid = false;
52635         this.clearMonitor();
52636         this.el.setStyle("z-index", "");
52637         if(this.collapseBtn){
52638             this.collapseBtn.show();
52639         }
52640         this.closeBtn.setStyle('display', this.closeBtnState);
52641         if(this.stickBtn){
52642             this.stickBtn.hide();
52643         }
52644         this.fireEvent("slidehide", this);
52645     },
52646
52647     slideIn : function(cb){
52648         if(!this.isSlid || this.el.hasActiveFx()){
52649             Roo.callback(cb);
52650             return;
52651         }
52652         this.isSlid = false;
52653         this.beforeSlide();
52654         this.el.slideOut(this.getSlideAnchor(), {
52655             callback: function(){
52656                 this.el.setLeftTop(-10000, -10000);
52657                 this.afterSlide();
52658                 this.afterSlideIn();
52659                 Roo.callback(cb);
52660             },
52661             scope: this,
52662             block: true
52663         });
52664     },
52665     
52666     slideInIf : function(e){
52667         if(!e.within(this.el)){
52668             this.slideIn();
52669         }
52670     },
52671
52672     animateCollapse : function(){
52673         this.beforeSlide();
52674         this.el.setStyle("z-index", 20000);
52675         var anchor = this.getSlideAnchor();
52676         this.el.slideOut(anchor, {
52677             callback : function(){
52678                 this.el.setStyle("z-index", "");
52679                 this.collapsedEl.slideIn(anchor, {duration:.3});
52680                 this.afterSlide();
52681                 this.el.setLocation(-10000,-10000);
52682                 this.el.hide();
52683                 this.fireEvent("collapsed", this);
52684             },
52685             scope: this,
52686             block: true
52687         });
52688     },
52689
52690     animateExpand : function(){
52691         this.beforeSlide();
52692         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52693         this.el.setStyle("z-index", 20000);
52694         this.collapsedEl.hide({
52695             duration:.1
52696         });
52697         this.el.slideIn(this.getSlideAnchor(), {
52698             callback : function(){
52699                 this.el.setStyle("z-index", "");
52700                 this.afterSlide();
52701                 if(this.split){
52702                     this.split.el.show();
52703                 }
52704                 this.fireEvent("invalidated", this);
52705                 this.fireEvent("expanded", this);
52706             },
52707             scope: this,
52708             block: true
52709         });
52710     },
52711
52712     anchors : {
52713         "west" : "left",
52714         "east" : "right",
52715         "north" : "top",
52716         "south" : "bottom"
52717     },
52718
52719     sanchors : {
52720         "west" : "l",
52721         "east" : "r",
52722         "north" : "t",
52723         "south" : "b"
52724     },
52725
52726     canchors : {
52727         "west" : "tl-tr",
52728         "east" : "tr-tl",
52729         "north" : "tl-bl",
52730         "south" : "bl-tl"
52731     },
52732
52733     getAnchor : function(){
52734         return this.anchors[this.position];
52735     },
52736
52737     getCollapseAnchor : function(){
52738         return this.canchors[this.position];
52739     },
52740
52741     getSlideAnchor : function(){
52742         return this.sanchors[this.position];
52743     },
52744
52745     getAlignAdj : function(){
52746         var cm = this.cmargins;
52747         switch(this.position){
52748             case "west":
52749                 return [0, 0];
52750             break;
52751             case "east":
52752                 return [0, 0];
52753             break;
52754             case "north":
52755                 return [0, 0];
52756             break;
52757             case "south":
52758                 return [0, 0];
52759             break;
52760         }
52761     },
52762
52763     getExpandAdj : function(){
52764         var c = this.collapsedEl, cm = this.cmargins;
52765         switch(this.position){
52766             case "west":
52767                 return [-(cm.right+c.getWidth()+cm.left), 0];
52768             break;
52769             case "east":
52770                 return [cm.right+c.getWidth()+cm.left, 0];
52771             break;
52772             case "north":
52773                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52774             break;
52775             case "south":
52776                 return [0, cm.top+cm.bottom+c.getHeight()];
52777             break;
52778         }
52779     }
52780 });/*
52781  * Based on:
52782  * Ext JS Library 1.1.1
52783  * Copyright(c) 2006-2007, Ext JS, LLC.
52784  *
52785  * Originally Released Under LGPL - original licence link has changed is not relivant.
52786  *
52787  * Fork - LGPL
52788  * <script type="text/javascript">
52789  */
52790 /*
52791  * These classes are private internal classes
52792  */
52793 Roo.CenterLayoutRegion = function(mgr, config){
52794     Roo.LayoutRegion.call(this, mgr, config, "center");
52795     this.visible = true;
52796     this.minWidth = config.minWidth || 20;
52797     this.minHeight = config.minHeight || 20;
52798 };
52799
52800 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52801     hide : function(){
52802         // center panel can't be hidden
52803     },
52804     
52805     show : function(){
52806         // center panel can't be hidden
52807     },
52808     
52809     getMinWidth: function(){
52810         return this.minWidth;
52811     },
52812     
52813     getMinHeight: function(){
52814         return this.minHeight;
52815     }
52816 });
52817
52818
52819 Roo.NorthLayoutRegion = function(mgr, config){
52820     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52821     if(this.split){
52822         this.split.placement = Roo.SplitBar.TOP;
52823         this.split.orientation = Roo.SplitBar.VERTICAL;
52824         this.split.el.addClass("x-layout-split-v");
52825     }
52826     var size = config.initialSize || config.height;
52827     if(typeof size != "undefined"){
52828         this.el.setHeight(size);
52829     }
52830 };
52831 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52832     orientation: Roo.SplitBar.VERTICAL,
52833     getBox : function(){
52834         if(this.collapsed){
52835             return this.collapsedEl.getBox();
52836         }
52837         var box = this.el.getBox();
52838         if(this.split){
52839             box.height += this.split.el.getHeight();
52840         }
52841         return box;
52842     },
52843     
52844     updateBox : function(box){
52845         if(this.split && !this.collapsed){
52846             box.height -= this.split.el.getHeight();
52847             this.split.el.setLeft(box.x);
52848             this.split.el.setTop(box.y+box.height);
52849             this.split.el.setWidth(box.width);
52850         }
52851         if(this.collapsed){
52852             this.updateBody(box.width, null);
52853         }
52854         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52855     }
52856 });
52857
52858 Roo.SouthLayoutRegion = function(mgr, config){
52859     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52860     if(this.split){
52861         this.split.placement = Roo.SplitBar.BOTTOM;
52862         this.split.orientation = Roo.SplitBar.VERTICAL;
52863         this.split.el.addClass("x-layout-split-v");
52864     }
52865     var size = config.initialSize || config.height;
52866     if(typeof size != "undefined"){
52867         this.el.setHeight(size);
52868     }
52869 };
52870 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52871     orientation: Roo.SplitBar.VERTICAL,
52872     getBox : function(){
52873         if(this.collapsed){
52874             return this.collapsedEl.getBox();
52875         }
52876         var box = this.el.getBox();
52877         if(this.split){
52878             var sh = this.split.el.getHeight();
52879             box.height += sh;
52880             box.y -= sh;
52881         }
52882         return box;
52883     },
52884     
52885     updateBox : function(box){
52886         if(this.split && !this.collapsed){
52887             var sh = this.split.el.getHeight();
52888             box.height -= sh;
52889             box.y += sh;
52890             this.split.el.setLeft(box.x);
52891             this.split.el.setTop(box.y-sh);
52892             this.split.el.setWidth(box.width);
52893         }
52894         if(this.collapsed){
52895             this.updateBody(box.width, null);
52896         }
52897         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52898     }
52899 });
52900
52901 Roo.EastLayoutRegion = function(mgr, config){
52902     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52903     if(this.split){
52904         this.split.placement = Roo.SplitBar.RIGHT;
52905         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52906         this.split.el.addClass("x-layout-split-h");
52907     }
52908     var size = config.initialSize || config.width;
52909     if(typeof size != "undefined"){
52910         this.el.setWidth(size);
52911     }
52912 };
52913 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52914     orientation: Roo.SplitBar.HORIZONTAL,
52915     getBox : function(){
52916         if(this.collapsed){
52917             return this.collapsedEl.getBox();
52918         }
52919         var box = this.el.getBox();
52920         if(this.split){
52921             var sw = this.split.el.getWidth();
52922             box.width += sw;
52923             box.x -= sw;
52924         }
52925         return box;
52926     },
52927
52928     updateBox : function(box){
52929         if(this.split && !this.collapsed){
52930             var sw = this.split.el.getWidth();
52931             box.width -= sw;
52932             this.split.el.setLeft(box.x);
52933             this.split.el.setTop(box.y);
52934             this.split.el.setHeight(box.height);
52935             box.x += sw;
52936         }
52937         if(this.collapsed){
52938             this.updateBody(null, box.height);
52939         }
52940         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52941     }
52942 });
52943
52944 Roo.WestLayoutRegion = function(mgr, config){
52945     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52946     if(this.split){
52947         this.split.placement = Roo.SplitBar.LEFT;
52948         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52949         this.split.el.addClass("x-layout-split-h");
52950     }
52951     var size = config.initialSize || config.width;
52952     if(typeof size != "undefined"){
52953         this.el.setWidth(size);
52954     }
52955 };
52956 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52957     orientation: Roo.SplitBar.HORIZONTAL,
52958     getBox : function(){
52959         if(this.collapsed){
52960             return this.collapsedEl.getBox();
52961         }
52962         var box = this.el.getBox();
52963         if(this.split){
52964             box.width += this.split.el.getWidth();
52965         }
52966         return box;
52967     },
52968     
52969     updateBox : function(box){
52970         if(this.split && !this.collapsed){
52971             var sw = this.split.el.getWidth();
52972             box.width -= sw;
52973             this.split.el.setLeft(box.x+box.width);
52974             this.split.el.setTop(box.y);
52975             this.split.el.setHeight(box.height);
52976         }
52977         if(this.collapsed){
52978             this.updateBody(null, box.height);
52979         }
52980         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52981     }
52982 });
52983 /*
52984  * Based on:
52985  * Ext JS Library 1.1.1
52986  * Copyright(c) 2006-2007, Ext JS, LLC.
52987  *
52988  * Originally Released Under LGPL - original licence link has changed is not relivant.
52989  *
52990  * Fork - LGPL
52991  * <script type="text/javascript">
52992  */
52993  
52994  
52995 /*
52996  * Private internal class for reading and applying state
52997  */
52998 Roo.LayoutStateManager = function(layout){
52999      // default empty state
53000      this.state = {
53001         north: {},
53002         south: {},
53003         east: {},
53004         west: {}       
53005     };
53006 };
53007
53008 Roo.LayoutStateManager.prototype = {
53009     init : function(layout, provider){
53010         this.provider = provider;
53011         var state = provider.get(layout.id+"-layout-state");
53012         if(state){
53013             var wasUpdating = layout.isUpdating();
53014             if(!wasUpdating){
53015                 layout.beginUpdate();
53016             }
53017             for(var key in state){
53018                 if(typeof state[key] != "function"){
53019                     var rstate = state[key];
53020                     var r = layout.getRegion(key);
53021                     if(r && rstate){
53022                         if(rstate.size){
53023                             r.resizeTo(rstate.size);
53024                         }
53025                         if(rstate.collapsed == true){
53026                             r.collapse(true);
53027                         }else{
53028                             r.expand(null, true);
53029                         }
53030                     }
53031                 }
53032             }
53033             if(!wasUpdating){
53034                 layout.endUpdate();
53035             }
53036             this.state = state; 
53037         }
53038         this.layout = layout;
53039         layout.on("regionresized", this.onRegionResized, this);
53040         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53041         layout.on("regionexpanded", this.onRegionExpanded, this);
53042     },
53043     
53044     storeState : function(){
53045         this.provider.set(this.layout.id+"-layout-state", this.state);
53046     },
53047     
53048     onRegionResized : function(region, newSize){
53049         this.state[region.getPosition()].size = newSize;
53050         this.storeState();
53051     },
53052     
53053     onRegionCollapsed : function(region){
53054         this.state[region.getPosition()].collapsed = true;
53055         this.storeState();
53056     },
53057     
53058     onRegionExpanded : function(region){
53059         this.state[region.getPosition()].collapsed = false;
53060         this.storeState();
53061     }
53062 };/*
53063  * Based on:
53064  * Ext JS Library 1.1.1
53065  * Copyright(c) 2006-2007, Ext JS, LLC.
53066  *
53067  * Originally Released Under LGPL - original licence link has changed is not relivant.
53068  *
53069  * Fork - LGPL
53070  * <script type="text/javascript">
53071  */
53072 /**
53073  * @class Roo.ContentPanel
53074  * @extends Roo.util.Observable
53075  * A basic ContentPanel element.
53076  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53077  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53078  * @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
53079  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53080  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53081  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53082  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53083  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53084  * @cfg {String} title          The title for this panel
53085  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53086  * @cfg {String} url            Calls {@link #setUrl} with this value
53087  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53088  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53089  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53090  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53091
53092  * @constructor
53093  * Create a new ContentPanel.
53094  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53095  * @param {String/Object} config A string to set only the title or a config object
53096  * @param {String} content (optional) Set the HTML content for this panel
53097  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53098  */
53099 Roo.ContentPanel = function(el, config, content){
53100     
53101      
53102     /*
53103     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53104         config = el;
53105         el = Roo.id();
53106     }
53107     if (config && config.parentLayout) { 
53108         el = config.parentLayout.el.createChild(); 
53109     }
53110     */
53111     if(el.autoCreate){ // xtype is available if this is called from factory
53112         config = el;
53113         el = Roo.id();
53114     }
53115     this.el = Roo.get(el);
53116     if(!this.el && config && config.autoCreate){
53117         if(typeof config.autoCreate == "object"){
53118             if(!config.autoCreate.id){
53119                 config.autoCreate.id = config.id||el;
53120             }
53121             this.el = Roo.DomHelper.append(document.body,
53122                         config.autoCreate, true);
53123         }else{
53124             this.el = Roo.DomHelper.append(document.body,
53125                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53126         }
53127     }
53128     this.closable = false;
53129     this.loaded = false;
53130     this.active = false;
53131     if(typeof config == "string"){
53132         this.title = config;
53133     }else{
53134         Roo.apply(this, config);
53135     }
53136     
53137     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53138         this.wrapEl = this.el.wrap();
53139         this.toolbar.container = this.el.insertSibling(false, 'before');
53140         this.toolbar = new Roo.Toolbar(this.toolbar);
53141     }
53142     
53143     // xtype created footer. - not sure if will work as we normally have to render first..
53144     if (this.footer && !this.footer.el && this.footer.xtype) {
53145         if (!this.wrapEl) {
53146             this.wrapEl = this.el.wrap();
53147         }
53148     
53149         this.footer.container = this.wrapEl.createChild();
53150          
53151         this.footer = Roo.factory(this.footer, Roo);
53152         
53153     }
53154     
53155     if(this.resizeEl){
53156         this.resizeEl = Roo.get(this.resizeEl, true);
53157     }else{
53158         this.resizeEl = this.el;
53159     }
53160     // handle view.xtype
53161     
53162  
53163     
53164     
53165     this.addEvents({
53166         /**
53167          * @event activate
53168          * Fires when this panel is activated. 
53169          * @param {Roo.ContentPanel} this
53170          */
53171         "activate" : true,
53172         /**
53173          * @event deactivate
53174          * Fires when this panel is activated. 
53175          * @param {Roo.ContentPanel} this
53176          */
53177         "deactivate" : true,
53178
53179         /**
53180          * @event resize
53181          * Fires when this panel is resized if fitToFrame is true.
53182          * @param {Roo.ContentPanel} this
53183          * @param {Number} width The width after any component adjustments
53184          * @param {Number} height The height after any component adjustments
53185          */
53186         "resize" : true,
53187         
53188          /**
53189          * @event render
53190          * Fires when this tab is created
53191          * @param {Roo.ContentPanel} this
53192          */
53193         "render" : true
53194         
53195         
53196         
53197     });
53198     
53199
53200     
53201     
53202     if(this.autoScroll){
53203         this.resizeEl.setStyle("overflow", "auto");
53204     } else {
53205         // fix randome scrolling
53206         this.el.on('scroll', function() {
53207             Roo.log('fix random scolling');
53208             this.scrollTo('top',0); 
53209         });
53210     }
53211     content = content || this.content;
53212     if(content){
53213         this.setContent(content);
53214     }
53215     if(config && config.url){
53216         this.setUrl(this.url, this.params, this.loadOnce);
53217     }
53218     
53219     
53220     
53221     Roo.ContentPanel.superclass.constructor.call(this);
53222     
53223     if (this.view && typeof(this.view.xtype) != 'undefined') {
53224         this.view.el = this.el.appendChild(document.createElement("div"));
53225         this.view = Roo.factory(this.view); 
53226         this.view.render  &&  this.view.render(false, '');  
53227     }
53228     
53229     
53230     this.fireEvent('render', this);
53231 };
53232
53233 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53234     tabTip:'',
53235     setRegion : function(region){
53236         this.region = region;
53237         if(region){
53238            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53239         }else{
53240            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53241         } 
53242     },
53243     
53244     /**
53245      * Returns the toolbar for this Panel if one was configured. 
53246      * @return {Roo.Toolbar} 
53247      */
53248     getToolbar : function(){
53249         return this.toolbar;
53250     },
53251     
53252     setActiveState : function(active){
53253         this.active = active;
53254         if(!active){
53255             this.fireEvent("deactivate", this);
53256         }else{
53257             this.fireEvent("activate", this);
53258         }
53259     },
53260     /**
53261      * Updates this panel's element
53262      * @param {String} content The new content
53263      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53264     */
53265     setContent : function(content, loadScripts){
53266         this.el.update(content, loadScripts);
53267     },
53268
53269     ignoreResize : function(w, h){
53270         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53271             return true;
53272         }else{
53273             this.lastSize = {width: w, height: h};
53274             return false;
53275         }
53276     },
53277     /**
53278      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53279      * @return {Roo.UpdateManager} The UpdateManager
53280      */
53281     getUpdateManager : function(){
53282         return this.el.getUpdateManager();
53283     },
53284      /**
53285      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53286      * @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:
53287 <pre><code>
53288 panel.load({
53289     url: "your-url.php",
53290     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53291     callback: yourFunction,
53292     scope: yourObject, //(optional scope)
53293     discardUrl: false,
53294     nocache: false,
53295     text: "Loading...",
53296     timeout: 30,
53297     scripts: false
53298 });
53299 </code></pre>
53300      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53301      * 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.
53302      * @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}
53303      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53304      * @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.
53305      * @return {Roo.ContentPanel} this
53306      */
53307     load : function(){
53308         var um = this.el.getUpdateManager();
53309         um.update.apply(um, arguments);
53310         return this;
53311     },
53312
53313
53314     /**
53315      * 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.
53316      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53317      * @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)
53318      * @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)
53319      * @return {Roo.UpdateManager} The UpdateManager
53320      */
53321     setUrl : function(url, params, loadOnce){
53322         if(this.refreshDelegate){
53323             this.removeListener("activate", this.refreshDelegate);
53324         }
53325         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53326         this.on("activate", this.refreshDelegate);
53327         return this.el.getUpdateManager();
53328     },
53329     
53330     _handleRefresh : function(url, params, loadOnce){
53331         if(!loadOnce || !this.loaded){
53332             var updater = this.el.getUpdateManager();
53333             updater.update(url, params, this._setLoaded.createDelegate(this));
53334         }
53335     },
53336     
53337     _setLoaded : function(){
53338         this.loaded = true;
53339     }, 
53340     
53341     /**
53342      * Returns this panel's id
53343      * @return {String} 
53344      */
53345     getId : function(){
53346         return this.el.id;
53347     },
53348     
53349     /** 
53350      * Returns this panel's element - used by regiosn to add.
53351      * @return {Roo.Element} 
53352      */
53353     getEl : function(){
53354         return this.wrapEl || this.el;
53355     },
53356     
53357     adjustForComponents : function(width, height)
53358     {
53359         //Roo.log('adjustForComponents ');
53360         if(this.resizeEl != this.el){
53361             width -= this.el.getFrameWidth('lr');
53362             height -= this.el.getFrameWidth('tb');
53363         }
53364         if(this.toolbar){
53365             var te = this.toolbar.getEl();
53366             height -= te.getHeight();
53367             te.setWidth(width);
53368         }
53369         if(this.footer){
53370             var te = this.footer.getEl();
53371             Roo.log("footer:" + te.getHeight());
53372             
53373             height -= te.getHeight();
53374             te.setWidth(width);
53375         }
53376         
53377         
53378         if(this.adjustments){
53379             width += this.adjustments[0];
53380             height += this.adjustments[1];
53381         }
53382         return {"width": width, "height": height};
53383     },
53384     
53385     setSize : function(width, height){
53386         if(this.fitToFrame && !this.ignoreResize(width, height)){
53387             if(this.fitContainer && this.resizeEl != this.el){
53388                 this.el.setSize(width, height);
53389             }
53390             var size = this.adjustForComponents(width, height);
53391             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53392             this.fireEvent('resize', this, size.width, size.height);
53393         }
53394     },
53395     
53396     /**
53397      * Returns this panel's title
53398      * @return {String} 
53399      */
53400     getTitle : function(){
53401         return this.title;
53402     },
53403     
53404     /**
53405      * Set this panel's title
53406      * @param {String} title
53407      */
53408     setTitle : function(title){
53409         this.title = title;
53410         if(this.region){
53411             this.region.updatePanelTitle(this, title);
53412         }
53413     },
53414     
53415     /**
53416      * Returns true is this panel was configured to be closable
53417      * @return {Boolean} 
53418      */
53419     isClosable : function(){
53420         return this.closable;
53421     },
53422     
53423     beforeSlide : function(){
53424         this.el.clip();
53425         this.resizeEl.clip();
53426     },
53427     
53428     afterSlide : function(){
53429         this.el.unclip();
53430         this.resizeEl.unclip();
53431     },
53432     
53433     /**
53434      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53435      *   Will fail silently if the {@link #setUrl} method has not been called.
53436      *   This does not activate the panel, just updates its content.
53437      */
53438     refresh : function(){
53439         if(this.refreshDelegate){
53440            this.loaded = false;
53441            this.refreshDelegate();
53442         }
53443     },
53444     
53445     /**
53446      * Destroys this panel
53447      */
53448     destroy : function(){
53449         this.el.removeAllListeners();
53450         var tempEl = document.createElement("span");
53451         tempEl.appendChild(this.el.dom);
53452         tempEl.innerHTML = "";
53453         this.el.remove();
53454         this.el = null;
53455     },
53456     
53457     /**
53458      * form - if the content panel contains a form - this is a reference to it.
53459      * @type {Roo.form.Form}
53460      */
53461     form : false,
53462     /**
53463      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53464      *    This contains a reference to it.
53465      * @type {Roo.View}
53466      */
53467     view : false,
53468     
53469       /**
53470      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53471      * <pre><code>
53472
53473 layout.addxtype({
53474        xtype : 'Form',
53475        items: [ .... ]
53476    }
53477 );
53478
53479 </code></pre>
53480      * @param {Object} cfg Xtype definition of item to add.
53481      */
53482     
53483     addxtype : function(cfg) {
53484         // add form..
53485         if (cfg.xtype.match(/^Form$/)) {
53486             
53487             var el;
53488             //if (this.footer) {
53489             //    el = this.footer.container.insertSibling(false, 'before');
53490             //} else {
53491                 el = this.el.createChild();
53492             //}
53493
53494             this.form = new  Roo.form.Form(cfg);
53495             
53496             
53497             if ( this.form.allItems.length) {
53498                 this.form.render(el.dom);
53499             }
53500             return this.form;
53501         }
53502         // should only have one of theses..
53503         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53504             // views.. should not be just added - used named prop 'view''
53505             
53506             cfg.el = this.el.appendChild(document.createElement("div"));
53507             // factory?
53508             
53509             var ret = new Roo.factory(cfg);
53510              
53511              ret.render && ret.render(false, ''); // render blank..
53512             this.view = ret;
53513             return ret;
53514         }
53515         return false;
53516     }
53517 });
53518
53519 /**
53520  * @class Roo.GridPanel
53521  * @extends Roo.ContentPanel
53522  * @constructor
53523  * Create a new GridPanel.
53524  * @param {Roo.grid.Grid} grid The grid for this panel
53525  * @param {String/Object} config A string to set only the panel's title, or a config object
53526  */
53527 Roo.GridPanel = function(grid, config){
53528     
53529   
53530     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53531         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53532         
53533     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53534     
53535     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53536     
53537     if(this.toolbar){
53538         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53539     }
53540     // xtype created footer. - not sure if will work as we normally have to render first..
53541     if (this.footer && !this.footer.el && this.footer.xtype) {
53542         
53543         this.footer.container = this.grid.getView().getFooterPanel(true);
53544         this.footer.dataSource = this.grid.dataSource;
53545         this.footer = Roo.factory(this.footer, Roo);
53546         
53547     }
53548     
53549     grid.monitorWindowResize = false; // turn off autosizing
53550     grid.autoHeight = false;
53551     grid.autoWidth = false;
53552     this.grid = grid;
53553     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53554 };
53555
53556 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53557     getId : function(){
53558         return this.grid.id;
53559     },
53560     
53561     /**
53562      * Returns the grid for this panel
53563      * @return {Roo.grid.Grid} 
53564      */
53565     getGrid : function(){
53566         return this.grid;    
53567     },
53568     
53569     setSize : function(width, height){
53570         if(!this.ignoreResize(width, height)){
53571             var grid = this.grid;
53572             var size = this.adjustForComponents(width, height);
53573             grid.getGridEl().setSize(size.width, size.height);
53574             grid.autoSize();
53575         }
53576     },
53577     
53578     beforeSlide : function(){
53579         this.grid.getView().scroller.clip();
53580     },
53581     
53582     afterSlide : function(){
53583         this.grid.getView().scroller.unclip();
53584     },
53585     
53586     destroy : function(){
53587         this.grid.destroy();
53588         delete this.grid;
53589         Roo.GridPanel.superclass.destroy.call(this); 
53590     }
53591 });
53592
53593
53594 /**
53595  * @class Roo.NestedLayoutPanel
53596  * @extends Roo.ContentPanel
53597  * @constructor
53598  * Create a new NestedLayoutPanel.
53599  * 
53600  * 
53601  * @param {Roo.BorderLayout} layout The layout for this panel
53602  * @param {String/Object} config A string to set only the title or a config object
53603  */
53604 Roo.NestedLayoutPanel = function(layout, config)
53605 {
53606     // construct with only one argument..
53607     /* FIXME - implement nicer consturctors
53608     if (layout.layout) {
53609         config = layout;
53610         layout = config.layout;
53611         delete config.layout;
53612     }
53613     if (layout.xtype && !layout.getEl) {
53614         // then layout needs constructing..
53615         layout = Roo.factory(layout, Roo);
53616     }
53617     */
53618     
53619     
53620     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53621     
53622     layout.monitorWindowResize = false; // turn off autosizing
53623     this.layout = layout;
53624     this.layout.getEl().addClass("x-layout-nested-layout");
53625     
53626     
53627     
53628     
53629 };
53630
53631 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53632
53633     setSize : function(width, height){
53634         if(!this.ignoreResize(width, height)){
53635             var size = this.adjustForComponents(width, height);
53636             var el = this.layout.getEl();
53637             el.setSize(size.width, size.height);
53638             var touch = el.dom.offsetWidth;
53639             this.layout.layout();
53640             // ie requires a double layout on the first pass
53641             if(Roo.isIE && !this.initialized){
53642                 this.initialized = true;
53643                 this.layout.layout();
53644             }
53645         }
53646     },
53647     
53648     // activate all subpanels if not currently active..
53649     
53650     setActiveState : function(active){
53651         this.active = active;
53652         if(!active){
53653             this.fireEvent("deactivate", this);
53654             return;
53655         }
53656         
53657         this.fireEvent("activate", this);
53658         // not sure if this should happen before or after..
53659         if (!this.layout) {
53660             return; // should not happen..
53661         }
53662         var reg = false;
53663         for (var r in this.layout.regions) {
53664             reg = this.layout.getRegion(r);
53665             if (reg.getActivePanel()) {
53666                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53667                 reg.setActivePanel(reg.getActivePanel());
53668                 continue;
53669             }
53670             if (!reg.panels.length) {
53671                 continue;
53672             }
53673             reg.showPanel(reg.getPanel(0));
53674         }
53675         
53676         
53677         
53678         
53679     },
53680     
53681     /**
53682      * Returns the nested BorderLayout for this panel
53683      * @return {Roo.BorderLayout} 
53684      */
53685     getLayout : function(){
53686         return this.layout;
53687     },
53688     
53689      /**
53690      * Adds a xtype elements to the layout of the nested panel
53691      * <pre><code>
53692
53693 panel.addxtype({
53694        xtype : 'ContentPanel',
53695        region: 'west',
53696        items: [ .... ]
53697    }
53698 );
53699
53700 panel.addxtype({
53701         xtype : 'NestedLayoutPanel',
53702         region: 'west',
53703         layout: {
53704            center: { },
53705            west: { }   
53706         },
53707         items : [ ... list of content panels or nested layout panels.. ]
53708    }
53709 );
53710 </code></pre>
53711      * @param {Object} cfg Xtype definition of item to add.
53712      */
53713     addxtype : function(cfg) {
53714         return this.layout.addxtype(cfg);
53715     
53716     }
53717 });
53718
53719 Roo.ScrollPanel = function(el, config, content){
53720     config = config || {};
53721     config.fitToFrame = true;
53722     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53723     
53724     this.el.dom.style.overflow = "hidden";
53725     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53726     this.el.removeClass("x-layout-inactive-content");
53727     this.el.on("mousewheel", this.onWheel, this);
53728
53729     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53730     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53731     up.unselectable(); down.unselectable();
53732     up.on("click", this.scrollUp, this);
53733     down.on("click", this.scrollDown, this);
53734     up.addClassOnOver("x-scroller-btn-over");
53735     down.addClassOnOver("x-scroller-btn-over");
53736     up.addClassOnClick("x-scroller-btn-click");
53737     down.addClassOnClick("x-scroller-btn-click");
53738     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53739
53740     this.resizeEl = this.el;
53741     this.el = wrap; this.up = up; this.down = down;
53742 };
53743
53744 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53745     increment : 100,
53746     wheelIncrement : 5,
53747     scrollUp : function(){
53748         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53749     },
53750
53751     scrollDown : function(){
53752         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53753     },
53754
53755     afterScroll : function(){
53756         var el = this.resizeEl;
53757         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53758         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53759         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53760     },
53761
53762     setSize : function(){
53763         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53764         this.afterScroll();
53765     },
53766
53767     onWheel : function(e){
53768         var d = e.getWheelDelta();
53769         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53770         this.afterScroll();
53771         e.stopEvent();
53772     },
53773
53774     setContent : function(content, loadScripts){
53775         this.resizeEl.update(content, loadScripts);
53776     }
53777
53778 });
53779
53780
53781
53782
53783
53784
53785
53786
53787
53788 /**
53789  * @class Roo.TreePanel
53790  * @extends Roo.ContentPanel
53791  * @constructor
53792  * Create a new TreePanel. - defaults to fit/scoll contents.
53793  * @param {String/Object} config A string to set only the panel's title, or a config object
53794  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53795  */
53796 Roo.TreePanel = function(config){
53797     var el = config.el;
53798     var tree = config.tree;
53799     delete config.tree; 
53800     delete config.el; // hopefull!
53801     
53802     // wrapper for IE7 strict & safari scroll issue
53803     
53804     var treeEl = el.createChild();
53805     config.resizeEl = treeEl;
53806     
53807     
53808     
53809     Roo.TreePanel.superclass.constructor.call(this, el, config);
53810  
53811  
53812     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53813     //console.log(tree);
53814     this.on('activate', function()
53815     {
53816         if (this.tree.rendered) {
53817             return;
53818         }
53819         //console.log('render tree');
53820         this.tree.render();
53821     });
53822     // this should not be needed.. - it's actually the 'el' that resizes?
53823     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53824     
53825     //this.on('resize',  function (cp, w, h) {
53826     //        this.tree.innerCt.setWidth(w);
53827     //        this.tree.innerCt.setHeight(h);
53828     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53829     //});
53830
53831         
53832     
53833 };
53834
53835 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53836     fitToFrame : true,
53837     autoScroll : true
53838 });
53839
53840
53841
53842
53843
53844
53845
53846
53847
53848
53849
53850 /*
53851  * Based on:
53852  * Ext JS Library 1.1.1
53853  * Copyright(c) 2006-2007, Ext JS, LLC.
53854  *
53855  * Originally Released Under LGPL - original licence link has changed is not relivant.
53856  *
53857  * Fork - LGPL
53858  * <script type="text/javascript">
53859  */
53860  
53861
53862 /**
53863  * @class Roo.ReaderLayout
53864  * @extends Roo.BorderLayout
53865  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53866  * center region containing two nested regions (a top one for a list view and one for item preview below),
53867  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53868  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53869  * expedites the setup of the overall layout and regions for this common application style.
53870  * Example:
53871  <pre><code>
53872 var reader = new Roo.ReaderLayout();
53873 var CP = Roo.ContentPanel;  // shortcut for adding
53874
53875 reader.beginUpdate();
53876 reader.add("north", new CP("north", "North"));
53877 reader.add("west", new CP("west", {title: "West"}));
53878 reader.add("east", new CP("east", {title: "East"}));
53879
53880 reader.regions.listView.add(new CP("listView", "List"));
53881 reader.regions.preview.add(new CP("preview", "Preview"));
53882 reader.endUpdate();
53883 </code></pre>
53884 * @constructor
53885 * Create a new ReaderLayout
53886 * @param {Object} config Configuration options
53887 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53888 * document.body if omitted)
53889 */
53890 Roo.ReaderLayout = function(config, renderTo){
53891     var c = config || {size:{}};
53892     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53893         north: c.north !== false ? Roo.apply({
53894             split:false,
53895             initialSize: 32,
53896             titlebar: false
53897         }, c.north) : false,
53898         west: c.west !== false ? Roo.apply({
53899             split:true,
53900             initialSize: 200,
53901             minSize: 175,
53902             maxSize: 400,
53903             titlebar: true,
53904             collapsible: true,
53905             animate: true,
53906             margins:{left:5,right:0,bottom:5,top:5},
53907             cmargins:{left:5,right:5,bottom:5,top:5}
53908         }, c.west) : false,
53909         east: c.east !== false ? Roo.apply({
53910             split:true,
53911             initialSize: 200,
53912             minSize: 175,
53913             maxSize: 400,
53914             titlebar: true,
53915             collapsible: true,
53916             animate: true,
53917             margins:{left:0,right:5,bottom:5,top:5},
53918             cmargins:{left:5,right:5,bottom:5,top:5}
53919         }, c.east) : false,
53920         center: Roo.apply({
53921             tabPosition: 'top',
53922             autoScroll:false,
53923             closeOnTab: true,
53924             titlebar:false,
53925             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53926         }, c.center)
53927     });
53928
53929     this.el.addClass('x-reader');
53930
53931     this.beginUpdate();
53932
53933     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53934         south: c.preview !== false ? Roo.apply({
53935             split:true,
53936             initialSize: 200,
53937             minSize: 100,
53938             autoScroll:true,
53939             collapsible:true,
53940             titlebar: true,
53941             cmargins:{top:5,left:0, right:0, bottom:0}
53942         }, c.preview) : false,
53943         center: Roo.apply({
53944             autoScroll:false,
53945             titlebar:false,
53946             minHeight:200
53947         }, c.listView)
53948     });
53949     this.add('center', new Roo.NestedLayoutPanel(inner,
53950             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53951
53952     this.endUpdate();
53953
53954     this.regions.preview = inner.getRegion('south');
53955     this.regions.listView = inner.getRegion('center');
53956 };
53957
53958 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53959  * Based on:
53960  * Ext JS Library 1.1.1
53961  * Copyright(c) 2006-2007, Ext JS, LLC.
53962  *
53963  * Originally Released Under LGPL - original licence link has changed is not relivant.
53964  *
53965  * Fork - LGPL
53966  * <script type="text/javascript">
53967  */
53968  
53969 /**
53970  * @class Roo.grid.Grid
53971  * @extends Roo.util.Observable
53972  * This class represents the primary interface of a component based grid control.
53973  * <br><br>Usage:<pre><code>
53974  var grid = new Roo.grid.Grid("my-container-id", {
53975      ds: myDataStore,
53976      cm: myColModel,
53977      selModel: mySelectionModel,
53978      autoSizeColumns: true,
53979      monitorWindowResize: false,
53980      trackMouseOver: true
53981  });
53982  // set any options
53983  grid.render();
53984  * </code></pre>
53985  * <b>Common Problems:</b><br/>
53986  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53987  * element will correct this<br/>
53988  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53989  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53990  * are unpredictable.<br/>
53991  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53992  * grid to calculate dimensions/offsets.<br/>
53993   * @constructor
53994  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53995  * The container MUST have some type of size defined for the grid to fill. The container will be
53996  * automatically set to position relative if it isn't already.
53997  * @param {Object} config A config object that sets properties on this grid.
53998  */
53999 Roo.grid.Grid = function(container, config){
54000         // initialize the container
54001         this.container = Roo.get(container);
54002         this.container.update("");
54003         this.container.setStyle("overflow", "hidden");
54004     this.container.addClass('x-grid-container');
54005
54006     this.id = this.container.id;
54007
54008     Roo.apply(this, config);
54009     // check and correct shorthanded configs
54010     if(this.ds){
54011         this.dataSource = this.ds;
54012         delete this.ds;
54013     }
54014     if(this.cm){
54015         this.colModel = this.cm;
54016         delete this.cm;
54017     }
54018     if(this.sm){
54019         this.selModel = this.sm;
54020         delete this.sm;
54021     }
54022
54023     if (this.selModel) {
54024         this.selModel = Roo.factory(this.selModel, Roo.grid);
54025         this.sm = this.selModel;
54026         this.sm.xmodule = this.xmodule || false;
54027     }
54028     if (typeof(this.colModel.config) == 'undefined') {
54029         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54030         this.cm = this.colModel;
54031         this.cm.xmodule = this.xmodule || false;
54032     }
54033     if (this.dataSource) {
54034         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54035         this.ds = this.dataSource;
54036         this.ds.xmodule = this.xmodule || false;
54037          
54038     }
54039     
54040     
54041     
54042     if(this.width){
54043         this.container.setWidth(this.width);
54044     }
54045
54046     if(this.height){
54047         this.container.setHeight(this.height);
54048     }
54049     /** @private */
54050         this.addEvents({
54051         // raw events
54052         /**
54053          * @event click
54054          * The raw click event for the entire grid.
54055          * @param {Roo.EventObject} e
54056          */
54057         "click" : true,
54058         /**
54059          * @event dblclick
54060          * The raw dblclick event for the entire grid.
54061          * @param {Roo.EventObject} e
54062          */
54063         "dblclick" : true,
54064         /**
54065          * @event contextmenu
54066          * The raw contextmenu event for the entire grid.
54067          * @param {Roo.EventObject} e
54068          */
54069         "contextmenu" : true,
54070         /**
54071          * @event mousedown
54072          * The raw mousedown event for the entire grid.
54073          * @param {Roo.EventObject} e
54074          */
54075         "mousedown" : true,
54076         /**
54077          * @event mouseup
54078          * The raw mouseup event for the entire grid.
54079          * @param {Roo.EventObject} e
54080          */
54081         "mouseup" : true,
54082         /**
54083          * @event mouseover
54084          * The raw mouseover event for the entire grid.
54085          * @param {Roo.EventObject} e
54086          */
54087         "mouseover" : true,
54088         /**
54089          * @event mouseout
54090          * The raw mouseout event for the entire grid.
54091          * @param {Roo.EventObject} e
54092          */
54093         "mouseout" : true,
54094         /**
54095          * @event keypress
54096          * The raw keypress event for the entire grid.
54097          * @param {Roo.EventObject} e
54098          */
54099         "keypress" : true,
54100         /**
54101          * @event keydown
54102          * The raw keydown event for the entire grid.
54103          * @param {Roo.EventObject} e
54104          */
54105         "keydown" : true,
54106
54107         // custom events
54108
54109         /**
54110          * @event cellclick
54111          * Fires when a cell is clicked
54112          * @param {Grid} this
54113          * @param {Number} rowIndex
54114          * @param {Number} columnIndex
54115          * @param {Roo.EventObject} e
54116          */
54117         "cellclick" : true,
54118         /**
54119          * @event celldblclick
54120          * Fires when a cell is double clicked
54121          * @param {Grid} this
54122          * @param {Number} rowIndex
54123          * @param {Number} columnIndex
54124          * @param {Roo.EventObject} e
54125          */
54126         "celldblclick" : true,
54127         /**
54128          * @event rowclick
54129          * Fires when a row is clicked
54130          * @param {Grid} this
54131          * @param {Number} rowIndex
54132          * @param {Roo.EventObject} e
54133          */
54134         "rowclick" : true,
54135         /**
54136          * @event rowdblclick
54137          * Fires when a row is double clicked
54138          * @param {Grid} this
54139          * @param {Number} rowIndex
54140          * @param {Roo.EventObject} e
54141          */
54142         "rowdblclick" : true,
54143         /**
54144          * @event headerclick
54145          * Fires when a header is clicked
54146          * @param {Grid} this
54147          * @param {Number} columnIndex
54148          * @param {Roo.EventObject} e
54149          */
54150         "headerclick" : true,
54151         /**
54152          * @event headerdblclick
54153          * Fires when a header cell is double clicked
54154          * @param {Grid} this
54155          * @param {Number} columnIndex
54156          * @param {Roo.EventObject} e
54157          */
54158         "headerdblclick" : true,
54159         /**
54160          * @event rowcontextmenu
54161          * Fires when a row is right clicked
54162          * @param {Grid} this
54163          * @param {Number} rowIndex
54164          * @param {Roo.EventObject} e
54165          */
54166         "rowcontextmenu" : true,
54167         /**
54168          * @event cellcontextmenu
54169          * Fires when a cell is right clicked
54170          * @param {Grid} this
54171          * @param {Number} rowIndex
54172          * @param {Number} cellIndex
54173          * @param {Roo.EventObject} e
54174          */
54175          "cellcontextmenu" : true,
54176         /**
54177          * @event headercontextmenu
54178          * Fires when a header is right clicked
54179          * @param {Grid} this
54180          * @param {Number} columnIndex
54181          * @param {Roo.EventObject} e
54182          */
54183         "headercontextmenu" : true,
54184         /**
54185          * @event bodyscroll
54186          * Fires when the body element is scrolled
54187          * @param {Number} scrollLeft
54188          * @param {Number} scrollTop
54189          */
54190         "bodyscroll" : true,
54191         /**
54192          * @event columnresize
54193          * Fires when the user resizes a column
54194          * @param {Number} columnIndex
54195          * @param {Number} newSize
54196          */
54197         "columnresize" : true,
54198         /**
54199          * @event columnmove
54200          * Fires when the user moves a column
54201          * @param {Number} oldIndex
54202          * @param {Number} newIndex
54203          */
54204         "columnmove" : true,
54205         /**
54206          * @event startdrag
54207          * Fires when row(s) start being dragged
54208          * @param {Grid} this
54209          * @param {Roo.GridDD} dd The drag drop object
54210          * @param {event} e The raw browser event
54211          */
54212         "startdrag" : true,
54213         /**
54214          * @event enddrag
54215          * Fires when a drag operation is complete
54216          * @param {Grid} this
54217          * @param {Roo.GridDD} dd The drag drop object
54218          * @param {event} e The raw browser event
54219          */
54220         "enddrag" : true,
54221         /**
54222          * @event dragdrop
54223          * Fires when dragged row(s) are dropped on a valid DD target
54224          * @param {Grid} this
54225          * @param {Roo.GridDD} dd The drag drop object
54226          * @param {String} targetId The target drag drop object
54227          * @param {event} e The raw browser event
54228          */
54229         "dragdrop" : true,
54230         /**
54231          * @event dragover
54232          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54233          * @param {Grid} this
54234          * @param {Roo.GridDD} dd The drag drop object
54235          * @param {String} targetId The target drag drop object
54236          * @param {event} e The raw browser event
54237          */
54238         "dragover" : true,
54239         /**
54240          * @event dragenter
54241          *  Fires when the dragged row(s) first cross another DD target while being dragged
54242          * @param {Grid} this
54243          * @param {Roo.GridDD} dd The drag drop object
54244          * @param {String} targetId The target drag drop object
54245          * @param {event} e The raw browser event
54246          */
54247         "dragenter" : true,
54248         /**
54249          * @event dragout
54250          * Fires when the dragged row(s) leave another DD target while being dragged
54251          * @param {Grid} this
54252          * @param {Roo.GridDD} dd The drag drop object
54253          * @param {String} targetId The target drag drop object
54254          * @param {event} e The raw browser event
54255          */
54256         "dragout" : true,
54257         /**
54258          * @event rowclass
54259          * Fires when a row is rendered, so you can change add a style to it.
54260          * @param {GridView} gridview   The grid view
54261          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54262          */
54263         'rowclass' : true,
54264
54265         /**
54266          * @event render
54267          * Fires when the grid is rendered
54268          * @param {Grid} grid
54269          */
54270         'render' : true
54271     });
54272
54273     Roo.grid.Grid.superclass.constructor.call(this);
54274 };
54275 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54276     
54277     /**
54278      * @cfg {String} ddGroup - drag drop group.
54279      */
54280
54281     /**
54282      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54283      */
54284     minColumnWidth : 25,
54285
54286     /**
54287      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54288      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54289      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54290      */
54291     autoSizeColumns : false,
54292
54293     /**
54294      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54295      */
54296     autoSizeHeaders : true,
54297
54298     /**
54299      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54300      */
54301     monitorWindowResize : true,
54302
54303     /**
54304      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54305      * rows measured to get a columns size. Default is 0 (all rows).
54306      */
54307     maxRowsToMeasure : 0,
54308
54309     /**
54310      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54311      */
54312     trackMouseOver : true,
54313
54314     /**
54315     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54316     */
54317     
54318     /**
54319     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54320     */
54321     enableDragDrop : false,
54322     
54323     /**
54324     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54325     */
54326     enableColumnMove : true,
54327     
54328     /**
54329     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54330     */
54331     enableColumnHide : true,
54332     
54333     /**
54334     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54335     */
54336     enableRowHeightSync : false,
54337     
54338     /**
54339     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54340     */
54341     stripeRows : true,
54342     
54343     /**
54344     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54345     */
54346     autoHeight : false,
54347
54348     /**
54349      * @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.
54350      */
54351     autoExpandColumn : false,
54352
54353     /**
54354     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54355     * Default is 50.
54356     */
54357     autoExpandMin : 50,
54358
54359     /**
54360     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54361     */
54362     autoExpandMax : 1000,
54363
54364     /**
54365     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54366     */
54367     view : null,
54368
54369     /**
54370     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54371     */
54372     loadMask : false,
54373     /**
54374     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54375     */
54376     dropTarget: false,
54377     
54378    
54379     
54380     // private
54381     rendered : false,
54382
54383     /**
54384     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54385     * of a fixed width. Default is false.
54386     */
54387     /**
54388     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54389     */
54390     /**
54391      * Called once after all setup has been completed and the grid is ready to be rendered.
54392      * @return {Roo.grid.Grid} this
54393      */
54394     render : function()
54395     {
54396         var c = this.container;
54397         // try to detect autoHeight/width mode
54398         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54399             this.autoHeight = true;
54400         }
54401         var view = this.getView();
54402         view.init(this);
54403
54404         c.on("click", this.onClick, this);
54405         c.on("dblclick", this.onDblClick, this);
54406         c.on("contextmenu", this.onContextMenu, this);
54407         c.on("keydown", this.onKeyDown, this);
54408         if (Roo.isTouch) {
54409             c.on("touchstart", this.onTouchStart, this);
54410         }
54411
54412         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54413
54414         this.getSelectionModel().init(this);
54415
54416         view.render();
54417
54418         if(this.loadMask){
54419             this.loadMask = new Roo.LoadMask(this.container,
54420                     Roo.apply({store:this.dataSource}, this.loadMask));
54421         }
54422         
54423         
54424         if (this.toolbar && this.toolbar.xtype) {
54425             this.toolbar.container = this.getView().getHeaderPanel(true);
54426             this.toolbar = new Roo.Toolbar(this.toolbar);
54427         }
54428         if (this.footer && this.footer.xtype) {
54429             this.footer.dataSource = this.getDataSource();
54430             this.footer.container = this.getView().getFooterPanel(true);
54431             this.footer = Roo.factory(this.footer, Roo);
54432         }
54433         if (this.dropTarget && this.dropTarget.xtype) {
54434             delete this.dropTarget.xtype;
54435             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54436         }
54437         
54438         
54439         this.rendered = true;
54440         this.fireEvent('render', this);
54441         return this;
54442     },
54443
54444         /**
54445          * Reconfigures the grid to use a different Store and Column Model.
54446          * The View will be bound to the new objects and refreshed.
54447          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54448          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54449          */
54450     reconfigure : function(dataSource, colModel){
54451         if(this.loadMask){
54452             this.loadMask.destroy();
54453             this.loadMask = new Roo.LoadMask(this.container,
54454                     Roo.apply({store:dataSource}, this.loadMask));
54455         }
54456         this.view.bind(dataSource, colModel);
54457         this.dataSource = dataSource;
54458         this.colModel = colModel;
54459         this.view.refresh(true);
54460     },
54461
54462     // private
54463     onKeyDown : function(e){
54464         this.fireEvent("keydown", e);
54465     },
54466
54467     /**
54468      * Destroy this grid.
54469      * @param {Boolean} removeEl True to remove the element
54470      */
54471     destroy : function(removeEl, keepListeners){
54472         if(this.loadMask){
54473             this.loadMask.destroy();
54474         }
54475         var c = this.container;
54476         c.removeAllListeners();
54477         this.view.destroy();
54478         this.colModel.purgeListeners();
54479         if(!keepListeners){
54480             this.purgeListeners();
54481         }
54482         c.update("");
54483         if(removeEl === true){
54484             c.remove();
54485         }
54486     },
54487
54488     // private
54489     processEvent : function(name, e){
54490         // does this fire select???
54491         //Roo.log('grid:processEvent '  + name);
54492         
54493         if (name != 'touchstart' ) {
54494             this.fireEvent(name, e);    
54495         }
54496         
54497         var t = e.getTarget();
54498         var v = this.view;
54499         var header = v.findHeaderIndex(t);
54500         if(header !== false){
54501             var ename = name == 'touchstart' ? 'click' : name;
54502              
54503             this.fireEvent("header" + ename, this, header, e);
54504         }else{
54505             var row = v.findRowIndex(t);
54506             var cell = v.findCellIndex(t);
54507             if (name == 'touchstart') {
54508                 // first touch is always a click.
54509                 // hopefull this happens after selection is updated.?
54510                 name = false;
54511                 
54512                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54513                     var cs = this.selModel.getSelectedCell();
54514                     if (row == cs[0] && cell == cs[1]){
54515                         name = 'dblclick';
54516                     }
54517                 }
54518                 if (typeof(this.selModel.getSelections) != 'undefined') {
54519                     var cs = this.selModel.getSelections();
54520                     var ds = this.dataSource;
54521                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54522                         name = 'dblclick';
54523                     }
54524                 }
54525                 if (!name) {
54526                     return;
54527                 }
54528             }
54529             
54530             
54531             if(row !== false){
54532                 this.fireEvent("row" + name, this, row, e);
54533                 if(cell !== false){
54534                     this.fireEvent("cell" + name, this, row, cell, e);
54535                 }
54536             }
54537         }
54538     },
54539
54540     // private
54541     onClick : function(e){
54542         this.processEvent("click", e);
54543     },
54544    // private
54545     onTouchStart : function(e){
54546         this.processEvent("touchstart", e);
54547     },
54548
54549     // private
54550     onContextMenu : function(e, t){
54551         this.processEvent("contextmenu", e);
54552     },
54553
54554     // private
54555     onDblClick : function(e){
54556         this.processEvent("dblclick", e);
54557     },
54558
54559     // private
54560     walkCells : function(row, col, step, fn, scope){
54561         var cm = this.colModel, clen = cm.getColumnCount();
54562         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54563         if(step < 0){
54564             if(col < 0){
54565                 row--;
54566                 first = false;
54567             }
54568             while(row >= 0){
54569                 if(!first){
54570                     col = clen-1;
54571                 }
54572                 first = false;
54573                 while(col >= 0){
54574                     if(fn.call(scope || this, row, col, cm) === true){
54575                         return [row, col];
54576                     }
54577                     col--;
54578                 }
54579                 row--;
54580             }
54581         } else {
54582             if(col >= clen){
54583                 row++;
54584                 first = false;
54585             }
54586             while(row < rlen){
54587                 if(!first){
54588                     col = 0;
54589                 }
54590                 first = false;
54591                 while(col < clen){
54592                     if(fn.call(scope || this, row, col, cm) === true){
54593                         return [row, col];
54594                     }
54595                     col++;
54596                 }
54597                 row++;
54598             }
54599         }
54600         return null;
54601     },
54602
54603     // private
54604     getSelections : function(){
54605         return this.selModel.getSelections();
54606     },
54607
54608     /**
54609      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54610      * but if manual update is required this method will initiate it.
54611      */
54612     autoSize : function(){
54613         if(this.rendered){
54614             this.view.layout();
54615             if(this.view.adjustForScroll){
54616                 this.view.adjustForScroll();
54617             }
54618         }
54619     },
54620
54621     /**
54622      * Returns the grid's underlying element.
54623      * @return {Element} The element
54624      */
54625     getGridEl : function(){
54626         return this.container;
54627     },
54628
54629     // private for compatibility, overridden by editor grid
54630     stopEditing : function(){},
54631
54632     /**
54633      * Returns the grid's SelectionModel.
54634      * @return {SelectionModel}
54635      */
54636     getSelectionModel : function(){
54637         if(!this.selModel){
54638             this.selModel = new Roo.grid.RowSelectionModel();
54639         }
54640         return this.selModel;
54641     },
54642
54643     /**
54644      * Returns the grid's DataSource.
54645      * @return {DataSource}
54646      */
54647     getDataSource : function(){
54648         return this.dataSource;
54649     },
54650
54651     /**
54652      * Returns the grid's ColumnModel.
54653      * @return {ColumnModel}
54654      */
54655     getColumnModel : function(){
54656         return this.colModel;
54657     },
54658
54659     /**
54660      * Returns the grid's GridView object.
54661      * @return {GridView}
54662      */
54663     getView : function(){
54664         if(!this.view){
54665             this.view = new Roo.grid.GridView(this.viewConfig);
54666         }
54667         return this.view;
54668     },
54669     /**
54670      * Called to get grid's drag proxy text, by default returns this.ddText.
54671      * @return {String}
54672      */
54673     getDragDropText : function(){
54674         var count = this.selModel.getCount();
54675         return String.format(this.ddText, count, count == 1 ? '' : 's');
54676     }
54677 });
54678 /**
54679  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54680  * %0 is replaced with the number of selected rows.
54681  * @type String
54682  */
54683 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54684  * Based on:
54685  * Ext JS Library 1.1.1
54686  * Copyright(c) 2006-2007, Ext JS, LLC.
54687  *
54688  * Originally Released Under LGPL - original licence link has changed is not relivant.
54689  *
54690  * Fork - LGPL
54691  * <script type="text/javascript">
54692  */
54693  
54694 Roo.grid.AbstractGridView = function(){
54695         this.grid = null;
54696         
54697         this.events = {
54698             "beforerowremoved" : true,
54699             "beforerowsinserted" : true,
54700             "beforerefresh" : true,
54701             "rowremoved" : true,
54702             "rowsinserted" : true,
54703             "rowupdated" : true,
54704             "refresh" : true
54705         };
54706     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54707 };
54708
54709 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54710     rowClass : "x-grid-row",
54711     cellClass : "x-grid-cell",
54712     tdClass : "x-grid-td",
54713     hdClass : "x-grid-hd",
54714     splitClass : "x-grid-hd-split",
54715     
54716     init: function(grid){
54717         this.grid = grid;
54718                 var cid = this.grid.getGridEl().id;
54719         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54720         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54721         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54722         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54723         },
54724         
54725     getColumnRenderers : function(){
54726         var renderers = [];
54727         var cm = this.grid.colModel;
54728         var colCount = cm.getColumnCount();
54729         for(var i = 0; i < colCount; i++){
54730             renderers[i] = cm.getRenderer(i);
54731         }
54732         return renderers;
54733     },
54734     
54735     getColumnIds : function(){
54736         var ids = [];
54737         var cm = this.grid.colModel;
54738         var colCount = cm.getColumnCount();
54739         for(var i = 0; i < colCount; i++){
54740             ids[i] = cm.getColumnId(i);
54741         }
54742         return ids;
54743     },
54744     
54745     getDataIndexes : function(){
54746         if(!this.indexMap){
54747             this.indexMap = this.buildIndexMap();
54748         }
54749         return this.indexMap.colToData;
54750     },
54751     
54752     getColumnIndexByDataIndex : function(dataIndex){
54753         if(!this.indexMap){
54754             this.indexMap = this.buildIndexMap();
54755         }
54756         return this.indexMap.dataToCol[dataIndex];
54757     },
54758     
54759     /**
54760      * Set a css style for a column dynamically. 
54761      * @param {Number} colIndex The index of the column
54762      * @param {String} name The css property name
54763      * @param {String} value The css value
54764      */
54765     setCSSStyle : function(colIndex, name, value){
54766         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54767         Roo.util.CSS.updateRule(selector, name, value);
54768     },
54769     
54770     generateRules : function(cm){
54771         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54772         Roo.util.CSS.removeStyleSheet(rulesId);
54773         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54774             var cid = cm.getColumnId(i);
54775             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54776                          this.tdSelector, cid, " {\n}\n",
54777                          this.hdSelector, cid, " {\n}\n",
54778                          this.splitSelector, cid, " {\n}\n");
54779         }
54780         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54781     }
54782 });/*
54783  * Based on:
54784  * Ext JS Library 1.1.1
54785  * Copyright(c) 2006-2007, Ext JS, LLC.
54786  *
54787  * Originally Released Under LGPL - original licence link has changed is not relivant.
54788  *
54789  * Fork - LGPL
54790  * <script type="text/javascript">
54791  */
54792
54793 // private
54794 // This is a support class used internally by the Grid components
54795 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54796     this.grid = grid;
54797     this.view = grid.getView();
54798     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54799     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54800     if(hd2){
54801         this.setHandleElId(Roo.id(hd));
54802         this.setOuterHandleElId(Roo.id(hd2));
54803     }
54804     this.scroll = false;
54805 };
54806 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54807     maxDragWidth: 120,
54808     getDragData : function(e){
54809         var t = Roo.lib.Event.getTarget(e);
54810         var h = this.view.findHeaderCell(t);
54811         if(h){
54812             return {ddel: h.firstChild, header:h};
54813         }
54814         return false;
54815     },
54816
54817     onInitDrag : function(e){
54818         this.view.headersDisabled = true;
54819         var clone = this.dragData.ddel.cloneNode(true);
54820         clone.id = Roo.id();
54821         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54822         this.proxy.update(clone);
54823         return true;
54824     },
54825
54826     afterValidDrop : function(){
54827         var v = this.view;
54828         setTimeout(function(){
54829             v.headersDisabled = false;
54830         }, 50);
54831     },
54832
54833     afterInvalidDrop : function(){
54834         var v = this.view;
54835         setTimeout(function(){
54836             v.headersDisabled = false;
54837         }, 50);
54838     }
54839 });
54840 /*
54841  * Based on:
54842  * Ext JS Library 1.1.1
54843  * Copyright(c) 2006-2007, Ext JS, LLC.
54844  *
54845  * Originally Released Under LGPL - original licence link has changed is not relivant.
54846  *
54847  * Fork - LGPL
54848  * <script type="text/javascript">
54849  */
54850 // private
54851 // This is a support class used internally by the Grid components
54852 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54853     this.grid = grid;
54854     this.view = grid.getView();
54855     // split the proxies so they don't interfere with mouse events
54856     this.proxyTop = Roo.DomHelper.append(document.body, {
54857         cls:"col-move-top", html:"&#160;"
54858     }, true);
54859     this.proxyBottom = Roo.DomHelper.append(document.body, {
54860         cls:"col-move-bottom", html:"&#160;"
54861     }, true);
54862     this.proxyTop.hide = this.proxyBottom.hide = function(){
54863         this.setLeftTop(-100,-100);
54864         this.setStyle("visibility", "hidden");
54865     };
54866     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54867     // temporarily disabled
54868     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54869     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54870 };
54871 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54872     proxyOffsets : [-4, -9],
54873     fly: Roo.Element.fly,
54874
54875     getTargetFromEvent : function(e){
54876         var t = Roo.lib.Event.getTarget(e);
54877         var cindex = this.view.findCellIndex(t);
54878         if(cindex !== false){
54879             return this.view.getHeaderCell(cindex);
54880         }
54881         return null;
54882     },
54883
54884     nextVisible : function(h){
54885         var v = this.view, cm = this.grid.colModel;
54886         h = h.nextSibling;
54887         while(h){
54888             if(!cm.isHidden(v.getCellIndex(h))){
54889                 return h;
54890             }
54891             h = h.nextSibling;
54892         }
54893         return null;
54894     },
54895
54896     prevVisible : function(h){
54897         var v = this.view, cm = this.grid.colModel;
54898         h = h.prevSibling;
54899         while(h){
54900             if(!cm.isHidden(v.getCellIndex(h))){
54901                 return h;
54902             }
54903             h = h.prevSibling;
54904         }
54905         return null;
54906     },
54907
54908     positionIndicator : function(h, n, e){
54909         var x = Roo.lib.Event.getPageX(e);
54910         var r = Roo.lib.Dom.getRegion(n.firstChild);
54911         var px, pt, py = r.top + this.proxyOffsets[1];
54912         if((r.right - x) <= (r.right-r.left)/2){
54913             px = r.right+this.view.borderWidth;
54914             pt = "after";
54915         }else{
54916             px = r.left;
54917             pt = "before";
54918         }
54919         var oldIndex = this.view.getCellIndex(h);
54920         var newIndex = this.view.getCellIndex(n);
54921
54922         if(this.grid.colModel.isFixed(newIndex)){
54923             return false;
54924         }
54925
54926         var locked = this.grid.colModel.isLocked(newIndex);
54927
54928         if(pt == "after"){
54929             newIndex++;
54930         }
54931         if(oldIndex < newIndex){
54932             newIndex--;
54933         }
54934         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54935             return false;
54936         }
54937         px +=  this.proxyOffsets[0];
54938         this.proxyTop.setLeftTop(px, py);
54939         this.proxyTop.show();
54940         if(!this.bottomOffset){
54941             this.bottomOffset = this.view.mainHd.getHeight();
54942         }
54943         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54944         this.proxyBottom.show();
54945         return pt;
54946     },
54947
54948     onNodeEnter : function(n, dd, e, data){
54949         if(data.header != n){
54950             this.positionIndicator(data.header, n, e);
54951         }
54952     },
54953
54954     onNodeOver : function(n, dd, e, data){
54955         var result = false;
54956         if(data.header != n){
54957             result = this.positionIndicator(data.header, n, e);
54958         }
54959         if(!result){
54960             this.proxyTop.hide();
54961             this.proxyBottom.hide();
54962         }
54963         return result ? this.dropAllowed : this.dropNotAllowed;
54964     },
54965
54966     onNodeOut : function(n, dd, e, data){
54967         this.proxyTop.hide();
54968         this.proxyBottom.hide();
54969     },
54970
54971     onNodeDrop : function(n, dd, e, data){
54972         var h = data.header;
54973         if(h != n){
54974             var cm = this.grid.colModel;
54975             var x = Roo.lib.Event.getPageX(e);
54976             var r = Roo.lib.Dom.getRegion(n.firstChild);
54977             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54978             var oldIndex = this.view.getCellIndex(h);
54979             var newIndex = this.view.getCellIndex(n);
54980             var locked = cm.isLocked(newIndex);
54981             if(pt == "after"){
54982                 newIndex++;
54983             }
54984             if(oldIndex < newIndex){
54985                 newIndex--;
54986             }
54987             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54988                 return false;
54989             }
54990             cm.setLocked(oldIndex, locked, true);
54991             cm.moveColumn(oldIndex, newIndex);
54992             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54993             return true;
54994         }
54995         return false;
54996     }
54997 });
54998 /*
54999  * Based on:
55000  * Ext JS Library 1.1.1
55001  * Copyright(c) 2006-2007, Ext JS, LLC.
55002  *
55003  * Originally Released Under LGPL - original licence link has changed is not relivant.
55004  *
55005  * Fork - LGPL
55006  * <script type="text/javascript">
55007  */
55008   
55009 /**
55010  * @class Roo.grid.GridView
55011  * @extends Roo.util.Observable
55012  *
55013  * @constructor
55014  * @param {Object} config
55015  */
55016 Roo.grid.GridView = function(config){
55017     Roo.grid.GridView.superclass.constructor.call(this);
55018     this.el = null;
55019
55020     Roo.apply(this, config);
55021 };
55022
55023 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55024
55025     unselectable :  'unselectable="on"',
55026     unselectableCls :  'x-unselectable',
55027     
55028     
55029     rowClass : "x-grid-row",
55030
55031     cellClass : "x-grid-col",
55032
55033     tdClass : "x-grid-td",
55034
55035     hdClass : "x-grid-hd",
55036
55037     splitClass : "x-grid-split",
55038
55039     sortClasses : ["sort-asc", "sort-desc"],
55040
55041     enableMoveAnim : false,
55042
55043     hlColor: "C3DAF9",
55044
55045     dh : Roo.DomHelper,
55046
55047     fly : Roo.Element.fly,
55048
55049     css : Roo.util.CSS,
55050
55051     borderWidth: 1,
55052
55053     splitOffset: 3,
55054
55055     scrollIncrement : 22,
55056
55057     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55058
55059     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55060
55061     bind : function(ds, cm){
55062         if(this.ds){
55063             this.ds.un("load", this.onLoad, this);
55064             this.ds.un("datachanged", this.onDataChange, this);
55065             this.ds.un("add", this.onAdd, this);
55066             this.ds.un("remove", this.onRemove, this);
55067             this.ds.un("update", this.onUpdate, this);
55068             this.ds.un("clear", this.onClear, this);
55069         }
55070         if(ds){
55071             ds.on("load", this.onLoad, this);
55072             ds.on("datachanged", this.onDataChange, this);
55073             ds.on("add", this.onAdd, this);
55074             ds.on("remove", this.onRemove, this);
55075             ds.on("update", this.onUpdate, this);
55076             ds.on("clear", this.onClear, this);
55077         }
55078         this.ds = ds;
55079
55080         if(this.cm){
55081             this.cm.un("widthchange", this.onColWidthChange, this);
55082             this.cm.un("headerchange", this.onHeaderChange, this);
55083             this.cm.un("hiddenchange", this.onHiddenChange, this);
55084             this.cm.un("columnmoved", this.onColumnMove, this);
55085             this.cm.un("columnlockchange", this.onColumnLock, this);
55086         }
55087         if(cm){
55088             this.generateRules(cm);
55089             cm.on("widthchange", this.onColWidthChange, this);
55090             cm.on("headerchange", this.onHeaderChange, this);
55091             cm.on("hiddenchange", this.onHiddenChange, this);
55092             cm.on("columnmoved", this.onColumnMove, this);
55093             cm.on("columnlockchange", this.onColumnLock, this);
55094         }
55095         this.cm = cm;
55096     },
55097
55098     init: function(grid){
55099         Roo.grid.GridView.superclass.init.call(this, grid);
55100
55101         this.bind(grid.dataSource, grid.colModel);
55102
55103         grid.on("headerclick", this.handleHeaderClick, this);
55104
55105         if(grid.trackMouseOver){
55106             grid.on("mouseover", this.onRowOver, this);
55107             grid.on("mouseout", this.onRowOut, this);
55108         }
55109         grid.cancelTextSelection = function(){};
55110         this.gridId = grid.id;
55111
55112         var tpls = this.templates || {};
55113
55114         if(!tpls.master){
55115             tpls.master = new Roo.Template(
55116                '<div class="x-grid" hidefocus="true">',
55117                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55118                   '<div class="x-grid-topbar"></div>',
55119                   '<div class="x-grid-scroller"><div></div></div>',
55120                   '<div class="x-grid-locked">',
55121                       '<div class="x-grid-header">{lockedHeader}</div>',
55122                       '<div class="x-grid-body">{lockedBody}</div>',
55123                   "</div>",
55124                   '<div class="x-grid-viewport">',
55125                       '<div class="x-grid-header">{header}</div>',
55126                       '<div class="x-grid-body">{body}</div>',
55127                   "</div>",
55128                   '<div class="x-grid-bottombar"></div>',
55129                  
55130                   '<div class="x-grid-resize-proxy">&#160;</div>',
55131                "</div>"
55132             );
55133             tpls.master.disableformats = true;
55134         }
55135
55136         if(!tpls.header){
55137             tpls.header = new Roo.Template(
55138                '<table border="0" cellspacing="0" cellpadding="0">',
55139                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55140                "</table>{splits}"
55141             );
55142             tpls.header.disableformats = true;
55143         }
55144         tpls.header.compile();
55145
55146         if(!tpls.hcell){
55147             tpls.hcell = new Roo.Template(
55148                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55149                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55150                 "</div></td>"
55151              );
55152              tpls.hcell.disableFormats = true;
55153         }
55154         tpls.hcell.compile();
55155
55156         if(!tpls.hsplit){
55157             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55158                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55159             tpls.hsplit.disableFormats = true;
55160         }
55161         tpls.hsplit.compile();
55162
55163         if(!tpls.body){
55164             tpls.body = new Roo.Template(
55165                '<table border="0" cellspacing="0" cellpadding="0">',
55166                "<tbody>{rows}</tbody>",
55167                "</table>"
55168             );
55169             tpls.body.disableFormats = true;
55170         }
55171         tpls.body.compile();
55172
55173         if(!tpls.row){
55174             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55175             tpls.row.disableFormats = true;
55176         }
55177         tpls.row.compile();
55178
55179         if(!tpls.cell){
55180             tpls.cell = new Roo.Template(
55181                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55182                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55183                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55184                 "</td>"
55185             );
55186             tpls.cell.disableFormats = true;
55187         }
55188         tpls.cell.compile();
55189
55190         this.templates = tpls;
55191     },
55192
55193     // remap these for backwards compat
55194     onColWidthChange : function(){
55195         this.updateColumns.apply(this, arguments);
55196     },
55197     onHeaderChange : function(){
55198         this.updateHeaders.apply(this, arguments);
55199     }, 
55200     onHiddenChange : function(){
55201         this.handleHiddenChange.apply(this, arguments);
55202     },
55203     onColumnMove : function(){
55204         this.handleColumnMove.apply(this, arguments);
55205     },
55206     onColumnLock : function(){
55207         this.handleLockChange.apply(this, arguments);
55208     },
55209
55210     onDataChange : function(){
55211         this.refresh();
55212         this.updateHeaderSortState();
55213     },
55214
55215     onClear : function(){
55216         this.refresh();
55217     },
55218
55219     onUpdate : function(ds, record){
55220         this.refreshRow(record);
55221     },
55222
55223     refreshRow : function(record){
55224         var ds = this.ds, index;
55225         if(typeof record == 'number'){
55226             index = record;
55227             record = ds.getAt(index);
55228         }else{
55229             index = ds.indexOf(record);
55230         }
55231         this.insertRows(ds, index, index, true);
55232         this.onRemove(ds, record, index+1, true);
55233         this.syncRowHeights(index, index);
55234         this.layout();
55235         this.fireEvent("rowupdated", this, index, record);
55236     },
55237
55238     onAdd : function(ds, records, index){
55239         this.insertRows(ds, index, index + (records.length-1));
55240     },
55241
55242     onRemove : function(ds, record, index, isUpdate){
55243         if(isUpdate !== true){
55244             this.fireEvent("beforerowremoved", this, index, record);
55245         }
55246         var bt = this.getBodyTable(), lt = this.getLockedTable();
55247         if(bt.rows[index]){
55248             bt.firstChild.removeChild(bt.rows[index]);
55249         }
55250         if(lt.rows[index]){
55251             lt.firstChild.removeChild(lt.rows[index]);
55252         }
55253         if(isUpdate !== true){
55254             this.stripeRows(index);
55255             this.syncRowHeights(index, index);
55256             this.layout();
55257             this.fireEvent("rowremoved", this, index, record);
55258         }
55259     },
55260
55261     onLoad : function(){
55262         this.scrollToTop();
55263     },
55264
55265     /**
55266      * Scrolls the grid to the top
55267      */
55268     scrollToTop : function(){
55269         if(this.scroller){
55270             this.scroller.dom.scrollTop = 0;
55271             this.syncScroll();
55272         }
55273     },
55274
55275     /**
55276      * Gets a panel in the header of the grid that can be used for toolbars etc.
55277      * After modifying the contents of this panel a call to grid.autoSize() may be
55278      * required to register any changes in size.
55279      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55280      * @return Roo.Element
55281      */
55282     getHeaderPanel : function(doShow){
55283         if(doShow){
55284             this.headerPanel.show();
55285         }
55286         return this.headerPanel;
55287     },
55288
55289     /**
55290      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55291      * After modifying the contents of this panel a call to grid.autoSize() may be
55292      * required to register any changes in size.
55293      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55294      * @return Roo.Element
55295      */
55296     getFooterPanel : function(doShow){
55297         if(doShow){
55298             this.footerPanel.show();
55299         }
55300         return this.footerPanel;
55301     },
55302
55303     initElements : function(){
55304         var E = Roo.Element;
55305         var el = this.grid.getGridEl().dom.firstChild;
55306         var cs = el.childNodes;
55307
55308         this.el = new E(el);
55309         
55310          this.focusEl = new E(el.firstChild);
55311         this.focusEl.swallowEvent("click", true);
55312         
55313         this.headerPanel = new E(cs[1]);
55314         this.headerPanel.enableDisplayMode("block");
55315
55316         this.scroller = new E(cs[2]);
55317         this.scrollSizer = new E(this.scroller.dom.firstChild);
55318
55319         this.lockedWrap = new E(cs[3]);
55320         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55321         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55322
55323         this.mainWrap = new E(cs[4]);
55324         this.mainHd = new E(this.mainWrap.dom.firstChild);
55325         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55326
55327         this.footerPanel = new E(cs[5]);
55328         this.footerPanel.enableDisplayMode("block");
55329
55330         this.resizeProxy = new E(cs[6]);
55331
55332         this.headerSelector = String.format(
55333            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55334            this.lockedHd.id, this.mainHd.id
55335         );
55336
55337         this.splitterSelector = String.format(
55338            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55339            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55340         );
55341     },
55342     idToCssName : function(s)
55343     {
55344         return s.replace(/[^a-z0-9]+/ig, '-');
55345     },
55346
55347     getHeaderCell : function(index){
55348         return Roo.DomQuery.select(this.headerSelector)[index];
55349     },
55350
55351     getHeaderCellMeasure : function(index){
55352         return this.getHeaderCell(index).firstChild;
55353     },
55354
55355     getHeaderCellText : function(index){
55356         return this.getHeaderCell(index).firstChild.firstChild;
55357     },
55358
55359     getLockedTable : function(){
55360         return this.lockedBody.dom.firstChild;
55361     },
55362
55363     getBodyTable : function(){
55364         return this.mainBody.dom.firstChild;
55365     },
55366
55367     getLockedRow : function(index){
55368         return this.getLockedTable().rows[index];
55369     },
55370
55371     getRow : function(index){
55372         return this.getBodyTable().rows[index];
55373     },
55374
55375     getRowComposite : function(index){
55376         if(!this.rowEl){
55377             this.rowEl = new Roo.CompositeElementLite();
55378         }
55379         var els = [], lrow, mrow;
55380         if(lrow = this.getLockedRow(index)){
55381             els.push(lrow);
55382         }
55383         if(mrow = this.getRow(index)){
55384             els.push(mrow);
55385         }
55386         this.rowEl.elements = els;
55387         return this.rowEl;
55388     },
55389     /**
55390      * Gets the 'td' of the cell
55391      * 
55392      * @param {Integer} rowIndex row to select
55393      * @param {Integer} colIndex column to select
55394      * 
55395      * @return {Object} 
55396      */
55397     getCell : function(rowIndex, colIndex){
55398         var locked = this.cm.getLockedCount();
55399         var source;
55400         if(colIndex < locked){
55401             source = this.lockedBody.dom.firstChild;
55402         }else{
55403             source = this.mainBody.dom.firstChild;
55404             colIndex -= locked;
55405         }
55406         return source.rows[rowIndex].childNodes[colIndex];
55407     },
55408
55409     getCellText : function(rowIndex, colIndex){
55410         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55411     },
55412
55413     getCellBox : function(cell){
55414         var b = this.fly(cell).getBox();
55415         if(Roo.isOpera){ // opera fails to report the Y
55416             b.y = cell.offsetTop + this.mainBody.getY();
55417         }
55418         return b;
55419     },
55420
55421     getCellIndex : function(cell){
55422         var id = String(cell.className).match(this.cellRE);
55423         if(id){
55424             return parseInt(id[1], 10);
55425         }
55426         return 0;
55427     },
55428
55429     findHeaderIndex : function(n){
55430         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55431         return r ? this.getCellIndex(r) : false;
55432     },
55433
55434     findHeaderCell : function(n){
55435         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55436         return r ? r : false;
55437     },
55438
55439     findRowIndex : function(n){
55440         if(!n){
55441             return false;
55442         }
55443         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55444         return r ? r.rowIndex : false;
55445     },
55446
55447     findCellIndex : function(node){
55448         var stop = this.el.dom;
55449         while(node && node != stop){
55450             if(this.findRE.test(node.className)){
55451                 return this.getCellIndex(node);
55452             }
55453             node = node.parentNode;
55454         }
55455         return false;
55456     },
55457
55458     getColumnId : function(index){
55459         return this.cm.getColumnId(index);
55460     },
55461
55462     getSplitters : function()
55463     {
55464         if(this.splitterSelector){
55465            return Roo.DomQuery.select(this.splitterSelector);
55466         }else{
55467             return null;
55468       }
55469     },
55470
55471     getSplitter : function(index){
55472         return this.getSplitters()[index];
55473     },
55474
55475     onRowOver : function(e, t){
55476         var row;
55477         if((row = this.findRowIndex(t)) !== false){
55478             this.getRowComposite(row).addClass("x-grid-row-over");
55479         }
55480     },
55481
55482     onRowOut : function(e, t){
55483         var row;
55484         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55485             this.getRowComposite(row).removeClass("x-grid-row-over");
55486         }
55487     },
55488
55489     renderHeaders : function(){
55490         var cm = this.cm;
55491         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55492         var cb = [], lb = [], sb = [], lsb = [], p = {};
55493         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55494             p.cellId = "x-grid-hd-0-" + i;
55495             p.splitId = "x-grid-csplit-0-" + i;
55496             p.id = cm.getColumnId(i);
55497             p.value = cm.getColumnHeader(i) || "";
55498             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55499             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55500             if(!cm.isLocked(i)){
55501                 cb[cb.length] = ct.apply(p);
55502                 sb[sb.length] = st.apply(p);
55503             }else{
55504                 lb[lb.length] = ct.apply(p);
55505                 lsb[lsb.length] = st.apply(p);
55506             }
55507         }
55508         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55509                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55510     },
55511
55512     updateHeaders : function(){
55513         var html = this.renderHeaders();
55514         this.lockedHd.update(html[0]);
55515         this.mainHd.update(html[1]);
55516     },
55517
55518     /**
55519      * Focuses the specified row.
55520      * @param {Number} row The row index
55521      */
55522     focusRow : function(row)
55523     {
55524         //Roo.log('GridView.focusRow');
55525         var x = this.scroller.dom.scrollLeft;
55526         this.focusCell(row, 0, false);
55527         this.scroller.dom.scrollLeft = x;
55528     },
55529
55530     /**
55531      * Focuses the specified cell.
55532      * @param {Number} row The row index
55533      * @param {Number} col The column index
55534      * @param {Boolean} hscroll false to disable horizontal scrolling
55535      */
55536     focusCell : function(row, col, hscroll)
55537     {
55538         //Roo.log('GridView.focusCell');
55539         var el = this.ensureVisible(row, col, hscroll);
55540         this.focusEl.alignTo(el, "tl-tl");
55541         if(Roo.isGecko){
55542             this.focusEl.focus();
55543         }else{
55544             this.focusEl.focus.defer(1, this.focusEl);
55545         }
55546     },
55547
55548     /**
55549      * Scrolls the specified cell into view
55550      * @param {Number} row The row index
55551      * @param {Number} col The column index
55552      * @param {Boolean} hscroll false to disable horizontal scrolling
55553      */
55554     ensureVisible : function(row, col, hscroll)
55555     {
55556         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55557         //return null; //disable for testing.
55558         if(typeof row != "number"){
55559             row = row.rowIndex;
55560         }
55561         if(row < 0 && row >= this.ds.getCount()){
55562             return  null;
55563         }
55564         col = (col !== undefined ? col : 0);
55565         var cm = this.grid.colModel;
55566         while(cm.isHidden(col)){
55567             col++;
55568         }
55569
55570         var el = this.getCell(row, col);
55571         if(!el){
55572             return null;
55573         }
55574         var c = this.scroller.dom;
55575
55576         var ctop = parseInt(el.offsetTop, 10);
55577         var cleft = parseInt(el.offsetLeft, 10);
55578         var cbot = ctop + el.offsetHeight;
55579         var cright = cleft + el.offsetWidth;
55580         
55581         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55582         var stop = parseInt(c.scrollTop, 10);
55583         var sleft = parseInt(c.scrollLeft, 10);
55584         var sbot = stop + ch;
55585         var sright = sleft + c.clientWidth;
55586         /*
55587         Roo.log('GridView.ensureVisible:' +
55588                 ' ctop:' + ctop +
55589                 ' c.clientHeight:' + c.clientHeight +
55590                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55591                 ' stop:' + stop +
55592                 ' cbot:' + cbot +
55593                 ' sbot:' + sbot +
55594                 ' ch:' + ch  
55595                 );
55596         */
55597         if(ctop < stop){
55598              c.scrollTop = ctop;
55599             //Roo.log("set scrolltop to ctop DISABLE?");
55600         }else if(cbot > sbot){
55601             //Roo.log("set scrolltop to cbot-ch");
55602             c.scrollTop = cbot-ch;
55603         }
55604         
55605         if(hscroll !== false){
55606             if(cleft < sleft){
55607                 c.scrollLeft = cleft;
55608             }else if(cright > sright){
55609                 c.scrollLeft = cright-c.clientWidth;
55610             }
55611         }
55612          
55613         return el;
55614     },
55615
55616     updateColumns : function(){
55617         this.grid.stopEditing();
55618         var cm = this.grid.colModel, colIds = this.getColumnIds();
55619         //var totalWidth = cm.getTotalWidth();
55620         var pos = 0;
55621         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55622             //if(cm.isHidden(i)) continue;
55623             var w = cm.getColumnWidth(i);
55624             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55625             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55626         }
55627         this.updateSplitters();
55628     },
55629
55630     generateRules : function(cm){
55631         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55632         Roo.util.CSS.removeStyleSheet(rulesId);
55633         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55634             var cid = cm.getColumnId(i);
55635             var align = '';
55636             if(cm.config[i].align){
55637                 align = 'text-align:'+cm.config[i].align+';';
55638             }
55639             var hidden = '';
55640             if(cm.isHidden(i)){
55641                 hidden = 'display:none;';
55642             }
55643             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55644             ruleBuf.push(
55645                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55646                     this.hdSelector, cid, " {\n", align, width, "}\n",
55647                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55648                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55649         }
55650         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55651     },
55652
55653     updateSplitters : function(){
55654         var cm = this.cm, s = this.getSplitters();
55655         if(s){ // splitters not created yet
55656             var pos = 0, locked = true;
55657             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55658                 if(cm.isHidden(i)) {
55659                     continue;
55660                 }
55661                 var w = cm.getColumnWidth(i); // make sure it's a number
55662                 if(!cm.isLocked(i) && locked){
55663                     pos = 0;
55664                     locked = false;
55665                 }
55666                 pos += w;
55667                 s[i].style.left = (pos-this.splitOffset) + "px";
55668             }
55669         }
55670     },
55671
55672     handleHiddenChange : function(colModel, colIndex, hidden){
55673         if(hidden){
55674             this.hideColumn(colIndex);
55675         }else{
55676             this.unhideColumn(colIndex);
55677         }
55678     },
55679
55680     hideColumn : function(colIndex){
55681         var cid = this.getColumnId(colIndex);
55682         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55683         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55684         if(Roo.isSafari){
55685             this.updateHeaders();
55686         }
55687         this.updateSplitters();
55688         this.layout();
55689     },
55690
55691     unhideColumn : function(colIndex){
55692         var cid = this.getColumnId(colIndex);
55693         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55694         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55695
55696         if(Roo.isSafari){
55697             this.updateHeaders();
55698         }
55699         this.updateSplitters();
55700         this.layout();
55701     },
55702
55703     insertRows : function(dm, firstRow, lastRow, isUpdate){
55704         if(firstRow == 0 && lastRow == dm.getCount()-1){
55705             this.refresh();
55706         }else{
55707             if(!isUpdate){
55708                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55709             }
55710             var s = this.getScrollState();
55711             var markup = this.renderRows(firstRow, lastRow);
55712             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55713             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55714             this.restoreScroll(s);
55715             if(!isUpdate){
55716                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55717                 this.syncRowHeights(firstRow, lastRow);
55718                 this.stripeRows(firstRow);
55719                 this.layout();
55720             }
55721         }
55722     },
55723
55724     bufferRows : function(markup, target, index){
55725         var before = null, trows = target.rows, tbody = target.tBodies[0];
55726         if(index < trows.length){
55727             before = trows[index];
55728         }
55729         var b = document.createElement("div");
55730         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55731         var rows = b.firstChild.rows;
55732         for(var i = 0, len = rows.length; i < len; i++){
55733             if(before){
55734                 tbody.insertBefore(rows[0], before);
55735             }else{
55736                 tbody.appendChild(rows[0]);
55737             }
55738         }
55739         b.innerHTML = "";
55740         b = null;
55741     },
55742
55743     deleteRows : function(dm, firstRow, lastRow){
55744         if(dm.getRowCount()<1){
55745             this.fireEvent("beforerefresh", this);
55746             this.mainBody.update("");
55747             this.lockedBody.update("");
55748             this.fireEvent("refresh", this);
55749         }else{
55750             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55751             var bt = this.getBodyTable();
55752             var tbody = bt.firstChild;
55753             var rows = bt.rows;
55754             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55755                 tbody.removeChild(rows[firstRow]);
55756             }
55757             this.stripeRows(firstRow);
55758             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55759         }
55760     },
55761
55762     updateRows : function(dataSource, firstRow, lastRow){
55763         var s = this.getScrollState();
55764         this.refresh();
55765         this.restoreScroll(s);
55766     },
55767
55768     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55769         if(!noRefresh){
55770            this.refresh();
55771         }
55772         this.updateHeaderSortState();
55773     },
55774
55775     getScrollState : function(){
55776         
55777         var sb = this.scroller.dom;
55778         return {left: sb.scrollLeft, top: sb.scrollTop};
55779     },
55780
55781     stripeRows : function(startRow){
55782         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55783             return;
55784         }
55785         startRow = startRow || 0;
55786         var rows = this.getBodyTable().rows;
55787         var lrows = this.getLockedTable().rows;
55788         var cls = ' x-grid-row-alt ';
55789         for(var i = startRow, len = rows.length; i < len; i++){
55790             var row = rows[i], lrow = lrows[i];
55791             var isAlt = ((i+1) % 2 == 0);
55792             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55793             if(isAlt == hasAlt){
55794                 continue;
55795             }
55796             if(isAlt){
55797                 row.className += " x-grid-row-alt";
55798             }else{
55799                 row.className = row.className.replace("x-grid-row-alt", "");
55800             }
55801             if(lrow){
55802                 lrow.className = row.className;
55803             }
55804         }
55805     },
55806
55807     restoreScroll : function(state){
55808         //Roo.log('GridView.restoreScroll');
55809         var sb = this.scroller.dom;
55810         sb.scrollLeft = state.left;
55811         sb.scrollTop = state.top;
55812         this.syncScroll();
55813     },
55814
55815     syncScroll : function(){
55816         //Roo.log('GridView.syncScroll');
55817         var sb = this.scroller.dom;
55818         var sh = this.mainHd.dom;
55819         var bs = this.mainBody.dom;
55820         var lv = this.lockedBody.dom;
55821         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55822         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55823     },
55824
55825     handleScroll : function(e){
55826         this.syncScroll();
55827         var sb = this.scroller.dom;
55828         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55829         e.stopEvent();
55830     },
55831
55832     handleWheel : function(e){
55833         var d = e.getWheelDelta();
55834         this.scroller.dom.scrollTop -= d*22;
55835         // set this here to prevent jumpy scrolling on large tables
55836         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55837         e.stopEvent();
55838     },
55839
55840     renderRows : function(startRow, endRow){
55841         // pull in all the crap needed to render rows
55842         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55843         var colCount = cm.getColumnCount();
55844
55845         if(ds.getCount() < 1){
55846             return ["", ""];
55847         }
55848
55849         // build a map for all the columns
55850         var cs = [];
55851         for(var i = 0; i < colCount; i++){
55852             var name = cm.getDataIndex(i);
55853             cs[i] = {
55854                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55855                 renderer : cm.getRenderer(i),
55856                 id : cm.getColumnId(i),
55857                 locked : cm.isLocked(i),
55858                 has_editor : cm.isCellEditable(i)
55859             };
55860         }
55861
55862         startRow = startRow || 0;
55863         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55864
55865         // records to render
55866         var rs = ds.getRange(startRow, endRow);
55867
55868         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55869     },
55870
55871     // As much as I hate to duplicate code, this was branched because FireFox really hates
55872     // [].join("") on strings. The performance difference was substantial enough to
55873     // branch this function
55874     doRender : Roo.isGecko ?
55875             function(cs, rs, ds, startRow, colCount, stripe){
55876                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55877                 // buffers
55878                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55879                 
55880                 var hasListener = this.grid.hasListener('rowclass');
55881                 var rowcfg = {};
55882                 for(var j = 0, len = rs.length; j < len; j++){
55883                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55884                     for(var i = 0; i < colCount; i++){
55885                         c = cs[i];
55886                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55887                         p.id = c.id;
55888                         p.css = p.attr = "";
55889                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55890                         if(p.value == undefined || p.value === "") {
55891                             p.value = "&#160;";
55892                         }
55893                         if(c.has_editor){
55894                             p.css += ' x-grid-editable-cell';
55895                         }
55896                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55897                             p.css +=  ' x-grid-dirty-cell';
55898                         }
55899                         var markup = ct.apply(p);
55900                         if(!c.locked){
55901                             cb+= markup;
55902                         }else{
55903                             lcb+= markup;
55904                         }
55905                     }
55906                     var alt = [];
55907                     if(stripe && ((rowIndex+1) % 2 == 0)){
55908                         alt.push("x-grid-row-alt")
55909                     }
55910                     if(r.dirty){
55911                         alt.push(  " x-grid-dirty-row");
55912                     }
55913                     rp.cells = lcb;
55914                     if(this.getRowClass){
55915                         alt.push(this.getRowClass(r, rowIndex));
55916                     }
55917                     if (hasListener) {
55918                         rowcfg = {
55919                              
55920                             record: r,
55921                             rowIndex : rowIndex,
55922                             rowClass : ''
55923                         };
55924                         this.grid.fireEvent('rowclass', this, rowcfg);
55925                         alt.push(rowcfg.rowClass);
55926                     }
55927                     rp.alt = alt.join(" ");
55928                     lbuf+= rt.apply(rp);
55929                     rp.cells = cb;
55930                     buf+=  rt.apply(rp);
55931                 }
55932                 return [lbuf, buf];
55933             } :
55934             function(cs, rs, ds, startRow, colCount, stripe){
55935                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55936                 // buffers
55937                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55938                 var hasListener = this.grid.hasListener('rowclass');
55939  
55940                 var rowcfg = {};
55941                 for(var j = 0, len = rs.length; j < len; j++){
55942                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55943                     for(var i = 0; i < colCount; i++){
55944                         c = cs[i];
55945                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55946                         p.id = c.id;
55947                         p.css = p.attr = "";
55948                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55949                         if(p.value == undefined || p.value === "") {
55950                             p.value = "&#160;";
55951                         }
55952                         //Roo.log(c);
55953                          if(c.has_editor){
55954                             p.css += ' x-grid-editable-cell';
55955                         }
55956                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55957                             p.css += ' x-grid-dirty-cell' 
55958                         }
55959                         
55960                         var markup = ct.apply(p);
55961                         if(!c.locked){
55962                             cb[cb.length] = markup;
55963                         }else{
55964                             lcb[lcb.length] = markup;
55965                         }
55966                     }
55967                     var alt = [];
55968                     if(stripe && ((rowIndex+1) % 2 == 0)){
55969                         alt.push( "x-grid-row-alt");
55970                     }
55971                     if(r.dirty){
55972                         alt.push(" x-grid-dirty-row");
55973                     }
55974                     rp.cells = lcb;
55975                     if(this.getRowClass){
55976                         alt.push( this.getRowClass(r, rowIndex));
55977                     }
55978                     if (hasListener) {
55979                         rowcfg = {
55980                              
55981                             record: r,
55982                             rowIndex : rowIndex,
55983                             rowClass : ''
55984                         };
55985                         this.grid.fireEvent('rowclass', this, rowcfg);
55986                         alt.push(rowcfg.rowClass);
55987                     }
55988                     
55989                     rp.alt = alt.join(" ");
55990                     rp.cells = lcb.join("");
55991                     lbuf[lbuf.length] = rt.apply(rp);
55992                     rp.cells = cb.join("");
55993                     buf[buf.length] =  rt.apply(rp);
55994                 }
55995                 return [lbuf.join(""), buf.join("")];
55996             },
55997
55998     renderBody : function(){
55999         var markup = this.renderRows();
56000         var bt = this.templates.body;
56001         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56002     },
56003
56004     /**
56005      * Refreshes the grid
56006      * @param {Boolean} headersToo
56007      */
56008     refresh : function(headersToo){
56009         this.fireEvent("beforerefresh", this);
56010         this.grid.stopEditing();
56011         var result = this.renderBody();
56012         this.lockedBody.update(result[0]);
56013         this.mainBody.update(result[1]);
56014         if(headersToo === true){
56015             this.updateHeaders();
56016             this.updateColumns();
56017             this.updateSplitters();
56018             this.updateHeaderSortState();
56019         }
56020         this.syncRowHeights();
56021         this.layout();
56022         this.fireEvent("refresh", this);
56023     },
56024
56025     handleColumnMove : function(cm, oldIndex, newIndex){
56026         this.indexMap = null;
56027         var s = this.getScrollState();
56028         this.refresh(true);
56029         this.restoreScroll(s);
56030         this.afterMove(newIndex);
56031     },
56032
56033     afterMove : function(colIndex){
56034         if(this.enableMoveAnim && Roo.enableFx){
56035             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56036         }
56037         // if multisort - fix sortOrder, and reload..
56038         if (this.grid.dataSource.multiSort) {
56039             // the we can call sort again..
56040             var dm = this.grid.dataSource;
56041             var cm = this.grid.colModel;
56042             var so = [];
56043             for(var i = 0; i < cm.config.length; i++ ) {
56044                 
56045                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56046                     continue; // dont' bother, it's not in sort list or being set.
56047                 }
56048                 
56049                 so.push(cm.config[i].dataIndex);
56050             };
56051             dm.sortOrder = so;
56052             dm.load(dm.lastOptions);
56053             
56054             
56055         }
56056         
56057     },
56058
56059     updateCell : function(dm, rowIndex, dataIndex){
56060         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56061         if(typeof colIndex == "undefined"){ // not present in grid
56062             return;
56063         }
56064         var cm = this.grid.colModel;
56065         var cell = this.getCell(rowIndex, colIndex);
56066         var cellText = this.getCellText(rowIndex, colIndex);
56067
56068         var p = {
56069             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56070             id : cm.getColumnId(colIndex),
56071             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56072         };
56073         var renderer = cm.getRenderer(colIndex);
56074         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56075         if(typeof val == "undefined" || val === "") {
56076             val = "&#160;";
56077         }
56078         cellText.innerHTML = val;
56079         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56080         this.syncRowHeights(rowIndex, rowIndex);
56081     },
56082
56083     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56084         var maxWidth = 0;
56085         if(this.grid.autoSizeHeaders){
56086             var h = this.getHeaderCellMeasure(colIndex);
56087             maxWidth = Math.max(maxWidth, h.scrollWidth);
56088         }
56089         var tb, index;
56090         if(this.cm.isLocked(colIndex)){
56091             tb = this.getLockedTable();
56092             index = colIndex;
56093         }else{
56094             tb = this.getBodyTable();
56095             index = colIndex - this.cm.getLockedCount();
56096         }
56097         if(tb && tb.rows){
56098             var rows = tb.rows;
56099             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56100             for(var i = 0; i < stopIndex; i++){
56101                 var cell = rows[i].childNodes[index].firstChild;
56102                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56103             }
56104         }
56105         return maxWidth + /*margin for error in IE*/ 5;
56106     },
56107     /**
56108      * Autofit a column to its content.
56109      * @param {Number} colIndex
56110      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56111      */
56112      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56113          if(this.cm.isHidden(colIndex)){
56114              return; // can't calc a hidden column
56115          }
56116         if(forceMinSize){
56117             var cid = this.cm.getColumnId(colIndex);
56118             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56119            if(this.grid.autoSizeHeaders){
56120                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56121            }
56122         }
56123         var newWidth = this.calcColumnWidth(colIndex);
56124         this.cm.setColumnWidth(colIndex,
56125             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56126         if(!suppressEvent){
56127             this.grid.fireEvent("columnresize", colIndex, newWidth);
56128         }
56129     },
56130
56131     /**
56132      * Autofits all columns to their content and then expands to fit any extra space in the grid
56133      */
56134      autoSizeColumns : function(){
56135         var cm = this.grid.colModel;
56136         var colCount = cm.getColumnCount();
56137         for(var i = 0; i < colCount; i++){
56138             this.autoSizeColumn(i, true, true);
56139         }
56140         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56141             this.fitColumns();
56142         }else{
56143             this.updateColumns();
56144             this.layout();
56145         }
56146     },
56147
56148     /**
56149      * Autofits all columns to the grid's width proportionate with their current size
56150      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56151      */
56152     fitColumns : function(reserveScrollSpace){
56153         var cm = this.grid.colModel;
56154         var colCount = cm.getColumnCount();
56155         var cols = [];
56156         var width = 0;
56157         var i, w;
56158         for (i = 0; i < colCount; i++){
56159             if(!cm.isHidden(i) && !cm.isFixed(i)){
56160                 w = cm.getColumnWidth(i);
56161                 cols.push(i);
56162                 cols.push(w);
56163                 width += w;
56164             }
56165         }
56166         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56167         if(reserveScrollSpace){
56168             avail -= 17;
56169         }
56170         var frac = (avail - cm.getTotalWidth())/width;
56171         while (cols.length){
56172             w = cols.pop();
56173             i = cols.pop();
56174             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56175         }
56176         this.updateColumns();
56177         this.layout();
56178     },
56179
56180     onRowSelect : function(rowIndex){
56181         var row = this.getRowComposite(rowIndex);
56182         row.addClass("x-grid-row-selected");
56183     },
56184
56185     onRowDeselect : function(rowIndex){
56186         var row = this.getRowComposite(rowIndex);
56187         row.removeClass("x-grid-row-selected");
56188     },
56189
56190     onCellSelect : function(row, col){
56191         var cell = this.getCell(row, col);
56192         if(cell){
56193             Roo.fly(cell).addClass("x-grid-cell-selected");
56194         }
56195     },
56196
56197     onCellDeselect : function(row, col){
56198         var cell = this.getCell(row, col);
56199         if(cell){
56200             Roo.fly(cell).removeClass("x-grid-cell-selected");
56201         }
56202     },
56203
56204     updateHeaderSortState : function(){
56205         
56206         // sort state can be single { field: xxx, direction : yyy}
56207         // or   { xxx=>ASC , yyy : DESC ..... }
56208         
56209         var mstate = {};
56210         if (!this.ds.multiSort) { 
56211             var state = this.ds.getSortState();
56212             if(!state){
56213                 return;
56214             }
56215             mstate[state.field] = state.direction;
56216             // FIXME... - this is not used here.. but might be elsewhere..
56217             this.sortState = state;
56218             
56219         } else {
56220             mstate = this.ds.sortToggle;
56221         }
56222         //remove existing sort classes..
56223         
56224         var sc = this.sortClasses;
56225         var hds = this.el.select(this.headerSelector).removeClass(sc);
56226         
56227         for(var f in mstate) {
56228         
56229             var sortColumn = this.cm.findColumnIndex(f);
56230             
56231             if(sortColumn != -1){
56232                 var sortDir = mstate[f];        
56233                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56234             }
56235         }
56236         
56237          
56238         
56239     },
56240
56241
56242     handleHeaderClick : function(g, index,e){
56243         
56244         Roo.log("header click");
56245         
56246         if (Roo.isTouch) {
56247             // touch events on header are handled by context
56248             this.handleHdCtx(g,index,e);
56249             return;
56250         }
56251         
56252         
56253         if(this.headersDisabled){
56254             return;
56255         }
56256         var dm = g.dataSource, cm = g.colModel;
56257         if(!cm.isSortable(index)){
56258             return;
56259         }
56260         g.stopEditing();
56261         
56262         if (dm.multiSort) {
56263             // update the sortOrder
56264             var so = [];
56265             for(var i = 0; i < cm.config.length; i++ ) {
56266                 
56267                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56268                     continue; // dont' bother, it's not in sort list or being set.
56269                 }
56270                 
56271                 so.push(cm.config[i].dataIndex);
56272             };
56273             dm.sortOrder = so;
56274         }
56275         
56276         
56277         dm.sort(cm.getDataIndex(index));
56278     },
56279
56280
56281     destroy : function(){
56282         if(this.colMenu){
56283             this.colMenu.removeAll();
56284             Roo.menu.MenuMgr.unregister(this.colMenu);
56285             this.colMenu.getEl().remove();
56286             delete this.colMenu;
56287         }
56288         if(this.hmenu){
56289             this.hmenu.removeAll();
56290             Roo.menu.MenuMgr.unregister(this.hmenu);
56291             this.hmenu.getEl().remove();
56292             delete this.hmenu;
56293         }
56294         if(this.grid.enableColumnMove){
56295             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56296             if(dds){
56297                 for(var dd in dds){
56298                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56299                         var elid = dds[dd].dragElId;
56300                         dds[dd].unreg();
56301                         Roo.get(elid).remove();
56302                     } else if(dds[dd].config.isTarget){
56303                         dds[dd].proxyTop.remove();
56304                         dds[dd].proxyBottom.remove();
56305                         dds[dd].unreg();
56306                     }
56307                     if(Roo.dd.DDM.locationCache[dd]){
56308                         delete Roo.dd.DDM.locationCache[dd];
56309                     }
56310                 }
56311                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56312             }
56313         }
56314         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56315         this.bind(null, null);
56316         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56317     },
56318
56319     handleLockChange : function(){
56320         this.refresh(true);
56321     },
56322
56323     onDenyColumnLock : function(){
56324
56325     },
56326
56327     onDenyColumnHide : function(){
56328
56329     },
56330
56331     handleHdMenuClick : function(item){
56332         var index = this.hdCtxIndex;
56333         var cm = this.cm, ds = this.ds;
56334         switch(item.id){
56335             case "asc":
56336                 ds.sort(cm.getDataIndex(index), "ASC");
56337                 break;
56338             case "desc":
56339                 ds.sort(cm.getDataIndex(index), "DESC");
56340                 break;
56341             case "lock":
56342                 var lc = cm.getLockedCount();
56343                 if(cm.getColumnCount(true) <= lc+1){
56344                     this.onDenyColumnLock();
56345                     return;
56346                 }
56347                 if(lc != index){
56348                     cm.setLocked(index, true, true);
56349                     cm.moveColumn(index, lc);
56350                     this.grid.fireEvent("columnmove", index, lc);
56351                 }else{
56352                     cm.setLocked(index, true);
56353                 }
56354             break;
56355             case "unlock":
56356                 var lc = cm.getLockedCount();
56357                 if((lc-1) != index){
56358                     cm.setLocked(index, false, true);
56359                     cm.moveColumn(index, lc-1);
56360                     this.grid.fireEvent("columnmove", index, lc-1);
56361                 }else{
56362                     cm.setLocked(index, false);
56363                 }
56364             break;
56365             case 'wider': // used to expand cols on touch..
56366             case 'narrow':
56367                 var cw = cm.getColumnWidth(index);
56368                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56369                 cw = Math.max(0, cw);
56370                 cw = Math.min(cw,4000);
56371                 cm.setColumnWidth(index, cw);
56372                 break;
56373                 
56374             default:
56375                 index = cm.getIndexById(item.id.substr(4));
56376                 if(index != -1){
56377                     if(item.checked && cm.getColumnCount(true) <= 1){
56378                         this.onDenyColumnHide();
56379                         return false;
56380                     }
56381                     cm.setHidden(index, item.checked);
56382                 }
56383         }
56384         return true;
56385     },
56386
56387     beforeColMenuShow : function(){
56388         var cm = this.cm,  colCount = cm.getColumnCount();
56389         this.colMenu.removeAll();
56390         for(var i = 0; i < colCount; i++){
56391             this.colMenu.add(new Roo.menu.CheckItem({
56392                 id: "col-"+cm.getColumnId(i),
56393                 text: cm.getColumnHeader(i),
56394                 checked: !cm.isHidden(i),
56395                 hideOnClick:false
56396             }));
56397         }
56398     },
56399
56400     handleHdCtx : function(g, index, e){
56401         e.stopEvent();
56402         var hd = this.getHeaderCell(index);
56403         this.hdCtxIndex = index;
56404         var ms = this.hmenu.items, cm = this.cm;
56405         ms.get("asc").setDisabled(!cm.isSortable(index));
56406         ms.get("desc").setDisabled(!cm.isSortable(index));
56407         if(this.grid.enableColLock !== false){
56408             ms.get("lock").setDisabled(cm.isLocked(index));
56409             ms.get("unlock").setDisabled(!cm.isLocked(index));
56410         }
56411         this.hmenu.show(hd, "tl-bl");
56412     },
56413
56414     handleHdOver : function(e){
56415         var hd = this.findHeaderCell(e.getTarget());
56416         if(hd && !this.headersDisabled){
56417             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56418                this.fly(hd).addClass("x-grid-hd-over");
56419             }
56420         }
56421     },
56422
56423     handleHdOut : function(e){
56424         var hd = this.findHeaderCell(e.getTarget());
56425         if(hd){
56426             this.fly(hd).removeClass("x-grid-hd-over");
56427         }
56428     },
56429
56430     handleSplitDblClick : function(e, t){
56431         var i = this.getCellIndex(t);
56432         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56433             this.autoSizeColumn(i, true);
56434             this.layout();
56435         }
56436     },
56437
56438     render : function(){
56439
56440         var cm = this.cm;
56441         var colCount = cm.getColumnCount();
56442
56443         if(this.grid.monitorWindowResize === true){
56444             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56445         }
56446         var header = this.renderHeaders();
56447         var body = this.templates.body.apply({rows:""});
56448         var html = this.templates.master.apply({
56449             lockedBody: body,
56450             body: body,
56451             lockedHeader: header[0],
56452             header: header[1]
56453         });
56454
56455         //this.updateColumns();
56456
56457         this.grid.getGridEl().dom.innerHTML = html;
56458
56459         this.initElements();
56460         
56461         // a kludge to fix the random scolling effect in webkit
56462         this.el.on("scroll", function() {
56463             this.el.dom.scrollTop=0; // hopefully not recursive..
56464         },this);
56465
56466         this.scroller.on("scroll", this.handleScroll, this);
56467         this.lockedBody.on("mousewheel", this.handleWheel, this);
56468         this.mainBody.on("mousewheel", this.handleWheel, this);
56469
56470         this.mainHd.on("mouseover", this.handleHdOver, this);
56471         this.mainHd.on("mouseout", this.handleHdOut, this);
56472         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56473                 {delegate: "."+this.splitClass});
56474
56475         this.lockedHd.on("mouseover", this.handleHdOver, this);
56476         this.lockedHd.on("mouseout", this.handleHdOut, this);
56477         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56478                 {delegate: "."+this.splitClass});
56479
56480         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56481             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56482         }
56483
56484         this.updateSplitters();
56485
56486         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56487             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56488             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56489         }
56490
56491         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56492             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56493             this.hmenu.add(
56494                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56495                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56496             );
56497             if(this.grid.enableColLock !== false){
56498                 this.hmenu.add('-',
56499                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56500                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56501                 );
56502             }
56503             if (Roo.isTouch) {
56504                  this.hmenu.add('-',
56505                     {id:"wider", text: this.columnsWiderText},
56506                     {id:"narrow", text: this.columnsNarrowText }
56507                 );
56508                 
56509                  
56510             }
56511             
56512             if(this.grid.enableColumnHide !== false){
56513
56514                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56515                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56516                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56517
56518                 this.hmenu.add('-',
56519                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56520                 );
56521             }
56522             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56523
56524             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56525         }
56526
56527         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56528             this.dd = new Roo.grid.GridDragZone(this.grid, {
56529                 ddGroup : this.grid.ddGroup || 'GridDD'
56530             });
56531             
56532         }
56533
56534         /*
56535         for(var i = 0; i < colCount; i++){
56536             if(cm.isHidden(i)){
56537                 this.hideColumn(i);
56538             }
56539             if(cm.config[i].align){
56540                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56541                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56542             }
56543         }*/
56544         
56545         this.updateHeaderSortState();
56546
56547         this.beforeInitialResize();
56548         this.layout(true);
56549
56550         // two part rendering gives faster view to the user
56551         this.renderPhase2.defer(1, this);
56552     },
56553
56554     renderPhase2 : function(){
56555         // render the rows now
56556         this.refresh();
56557         if(this.grid.autoSizeColumns){
56558             this.autoSizeColumns();
56559         }
56560     },
56561
56562     beforeInitialResize : function(){
56563
56564     },
56565
56566     onColumnSplitterMoved : function(i, w){
56567         this.userResized = true;
56568         var cm = this.grid.colModel;
56569         cm.setColumnWidth(i, w, true);
56570         var cid = cm.getColumnId(i);
56571         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56572         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56573         this.updateSplitters();
56574         this.layout();
56575         this.grid.fireEvent("columnresize", i, w);
56576     },
56577
56578     syncRowHeights : function(startIndex, endIndex){
56579         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56580             startIndex = startIndex || 0;
56581             var mrows = this.getBodyTable().rows;
56582             var lrows = this.getLockedTable().rows;
56583             var len = mrows.length-1;
56584             endIndex = Math.min(endIndex || len, len);
56585             for(var i = startIndex; i <= endIndex; i++){
56586                 var m = mrows[i], l = lrows[i];
56587                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56588                 m.style.height = l.style.height = h + "px";
56589             }
56590         }
56591     },
56592
56593     layout : function(initialRender, is2ndPass){
56594         var g = this.grid;
56595         var auto = g.autoHeight;
56596         var scrollOffset = 16;
56597         var c = g.getGridEl(), cm = this.cm,
56598                 expandCol = g.autoExpandColumn,
56599                 gv = this;
56600         //c.beginMeasure();
56601
56602         if(!c.dom.offsetWidth){ // display:none?
56603             if(initialRender){
56604                 this.lockedWrap.show();
56605                 this.mainWrap.show();
56606             }
56607             return;
56608         }
56609
56610         var hasLock = this.cm.isLocked(0);
56611
56612         var tbh = this.headerPanel.getHeight();
56613         var bbh = this.footerPanel.getHeight();
56614
56615         if(auto){
56616             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56617             var newHeight = ch + c.getBorderWidth("tb");
56618             if(g.maxHeight){
56619                 newHeight = Math.min(g.maxHeight, newHeight);
56620             }
56621             c.setHeight(newHeight);
56622         }
56623
56624         if(g.autoWidth){
56625             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56626         }
56627
56628         var s = this.scroller;
56629
56630         var csize = c.getSize(true);
56631
56632         this.el.setSize(csize.width, csize.height);
56633
56634         this.headerPanel.setWidth(csize.width);
56635         this.footerPanel.setWidth(csize.width);
56636
56637         var hdHeight = this.mainHd.getHeight();
56638         var vw = csize.width;
56639         var vh = csize.height - (tbh + bbh);
56640
56641         s.setSize(vw, vh);
56642
56643         var bt = this.getBodyTable();
56644         
56645         if(cm.getLockedCount() == cm.config.length){
56646             bt = this.getLockedTable();
56647         }
56648         
56649         var ltWidth = hasLock ?
56650                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56651
56652         var scrollHeight = bt.offsetHeight;
56653         var scrollWidth = ltWidth + bt.offsetWidth;
56654         var vscroll = false, hscroll = false;
56655
56656         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56657
56658         var lw = this.lockedWrap, mw = this.mainWrap;
56659         var lb = this.lockedBody, mb = this.mainBody;
56660
56661         setTimeout(function(){
56662             var t = s.dom.offsetTop;
56663             var w = s.dom.clientWidth,
56664                 h = s.dom.clientHeight;
56665
56666             lw.setTop(t);
56667             lw.setSize(ltWidth, h);
56668
56669             mw.setLeftTop(ltWidth, t);
56670             mw.setSize(w-ltWidth, h);
56671
56672             lb.setHeight(h-hdHeight);
56673             mb.setHeight(h-hdHeight);
56674
56675             if(is2ndPass !== true && !gv.userResized && expandCol){
56676                 // high speed resize without full column calculation
56677                 
56678                 var ci = cm.getIndexById(expandCol);
56679                 if (ci < 0) {
56680                     ci = cm.findColumnIndex(expandCol);
56681                 }
56682                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56683                 var expandId = cm.getColumnId(ci);
56684                 var  tw = cm.getTotalWidth(false);
56685                 var currentWidth = cm.getColumnWidth(ci);
56686                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56687                 if(currentWidth != cw){
56688                     cm.setColumnWidth(ci, cw, true);
56689                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56690                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56691                     gv.updateSplitters();
56692                     gv.layout(false, true);
56693                 }
56694             }
56695
56696             if(initialRender){
56697                 lw.show();
56698                 mw.show();
56699             }
56700             //c.endMeasure();
56701         }, 10);
56702     },
56703
56704     onWindowResize : function(){
56705         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56706             return;
56707         }
56708         this.layout();
56709     },
56710
56711     appendFooter : function(parentEl){
56712         return null;
56713     },
56714
56715     sortAscText : "Sort Ascending",
56716     sortDescText : "Sort Descending",
56717     lockText : "Lock Column",
56718     unlockText : "Unlock Column",
56719     columnsText : "Columns",
56720  
56721     columnsWiderText : "Wider",
56722     columnsNarrowText : "Thinner"
56723 });
56724
56725
56726 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56727     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56728     this.proxy.el.addClass('x-grid3-col-dd');
56729 };
56730
56731 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56732     handleMouseDown : function(e){
56733
56734     },
56735
56736     callHandleMouseDown : function(e){
56737         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56738     }
56739 });
56740 /*
56741  * Based on:
56742  * Ext JS Library 1.1.1
56743  * Copyright(c) 2006-2007, Ext JS, LLC.
56744  *
56745  * Originally Released Under LGPL - original licence link has changed is not relivant.
56746  *
56747  * Fork - LGPL
56748  * <script type="text/javascript">
56749  */
56750  
56751 // private
56752 // This is a support class used internally by the Grid components
56753 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56754     this.grid = grid;
56755     this.view = grid.getView();
56756     this.proxy = this.view.resizeProxy;
56757     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56758         "gridSplitters" + this.grid.getGridEl().id, {
56759         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56760     });
56761     this.setHandleElId(Roo.id(hd));
56762     this.setOuterHandleElId(Roo.id(hd2));
56763     this.scroll = false;
56764 };
56765 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56766     fly: Roo.Element.fly,
56767
56768     b4StartDrag : function(x, y){
56769         this.view.headersDisabled = true;
56770         this.proxy.setHeight(this.view.mainWrap.getHeight());
56771         var w = this.cm.getColumnWidth(this.cellIndex);
56772         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56773         this.resetConstraints();
56774         this.setXConstraint(minw, 1000);
56775         this.setYConstraint(0, 0);
56776         this.minX = x - minw;
56777         this.maxX = x + 1000;
56778         this.startPos = x;
56779         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56780     },
56781
56782
56783     handleMouseDown : function(e){
56784         ev = Roo.EventObject.setEvent(e);
56785         var t = this.fly(ev.getTarget());
56786         if(t.hasClass("x-grid-split")){
56787             this.cellIndex = this.view.getCellIndex(t.dom);
56788             this.split = t.dom;
56789             this.cm = this.grid.colModel;
56790             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56791                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56792             }
56793         }
56794     },
56795
56796     endDrag : function(e){
56797         this.view.headersDisabled = false;
56798         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56799         var diff = endX - this.startPos;
56800         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56801     },
56802
56803     autoOffset : function(){
56804         this.setDelta(0,0);
56805     }
56806 });/*
56807  * Based on:
56808  * Ext JS Library 1.1.1
56809  * Copyright(c) 2006-2007, Ext JS, LLC.
56810  *
56811  * Originally Released Under LGPL - original licence link has changed is not relivant.
56812  *
56813  * Fork - LGPL
56814  * <script type="text/javascript">
56815  */
56816  
56817 // private
56818 // This is a support class used internally by the Grid components
56819 Roo.grid.GridDragZone = function(grid, config){
56820     this.view = grid.getView();
56821     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56822     if(this.view.lockedBody){
56823         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56824         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56825     }
56826     this.scroll = false;
56827     this.grid = grid;
56828     this.ddel = document.createElement('div');
56829     this.ddel.className = 'x-grid-dd-wrap';
56830 };
56831
56832 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56833     ddGroup : "GridDD",
56834
56835     getDragData : function(e){
56836         var t = Roo.lib.Event.getTarget(e);
56837         var rowIndex = this.view.findRowIndex(t);
56838         var sm = this.grid.selModel;
56839             
56840         //Roo.log(rowIndex);
56841         
56842         if (sm.getSelectedCell) {
56843             // cell selection..
56844             if (!sm.getSelectedCell()) {
56845                 return false;
56846             }
56847             if (rowIndex != sm.getSelectedCell()[0]) {
56848                 return false;
56849             }
56850         
56851         }
56852         
56853         if(rowIndex !== false){
56854             
56855             // if editorgrid.. 
56856             
56857             
56858             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56859                
56860             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56861               //  
56862             //}
56863             if (e.hasModifier()){
56864                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56865             }
56866             
56867             Roo.log("getDragData");
56868             
56869             return {
56870                 grid: this.grid,
56871                 ddel: this.ddel,
56872                 rowIndex: rowIndex,
56873                 selections:sm.getSelections ? sm.getSelections() : (
56874                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56875                 )
56876             };
56877         }
56878         return false;
56879     },
56880
56881     onInitDrag : function(e){
56882         var data = this.dragData;
56883         this.ddel.innerHTML = this.grid.getDragDropText();
56884         this.proxy.update(this.ddel);
56885         // fire start drag?
56886     },
56887
56888     afterRepair : function(){
56889         this.dragging = false;
56890     },
56891
56892     getRepairXY : function(e, data){
56893         return false;
56894     },
56895
56896     onEndDrag : function(data, e){
56897         // fire end drag?
56898     },
56899
56900     onValidDrop : function(dd, e, id){
56901         // fire drag drop?
56902         this.hideProxy();
56903     },
56904
56905     beforeInvalidDrop : function(e, id){
56906
56907     }
56908 });/*
56909  * Based on:
56910  * Ext JS Library 1.1.1
56911  * Copyright(c) 2006-2007, Ext JS, LLC.
56912  *
56913  * Originally Released Under LGPL - original licence link has changed is not relivant.
56914  *
56915  * Fork - LGPL
56916  * <script type="text/javascript">
56917  */
56918  
56919
56920 /**
56921  * @class Roo.grid.ColumnModel
56922  * @extends Roo.util.Observable
56923  * This is the default implementation of a ColumnModel used by the Grid. It defines
56924  * the columns in the grid.
56925  * <br>Usage:<br>
56926  <pre><code>
56927  var colModel = new Roo.grid.ColumnModel([
56928         {header: "Ticker", width: 60, sortable: true, locked: true},
56929         {header: "Company Name", width: 150, sortable: true},
56930         {header: "Market Cap.", width: 100, sortable: true},
56931         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56932         {header: "Employees", width: 100, sortable: true, resizable: false}
56933  ]);
56934  </code></pre>
56935  * <p>
56936  
56937  * The config options listed for this class are options which may appear in each
56938  * individual column definition.
56939  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56940  * @constructor
56941  * @param {Object} config An Array of column config objects. See this class's
56942  * config objects for details.
56943 */
56944 Roo.grid.ColumnModel = function(config){
56945         /**
56946      * The config passed into the constructor
56947      */
56948     this.config = config;
56949     this.lookup = {};
56950
56951     // if no id, create one
56952     // if the column does not have a dataIndex mapping,
56953     // map it to the order it is in the config
56954     for(var i = 0, len = config.length; i < len; i++){
56955         var c = config[i];
56956         if(typeof c.dataIndex == "undefined"){
56957             c.dataIndex = i;
56958         }
56959         if(typeof c.renderer == "string"){
56960             c.renderer = Roo.util.Format[c.renderer];
56961         }
56962         if(typeof c.id == "undefined"){
56963             c.id = Roo.id();
56964         }
56965         if(c.editor && c.editor.xtype){
56966             c.editor  = Roo.factory(c.editor, Roo.grid);
56967         }
56968         if(c.editor && c.editor.isFormField){
56969             c.editor = new Roo.grid.GridEditor(c.editor);
56970         }
56971         this.lookup[c.id] = c;
56972     }
56973
56974     /**
56975      * The width of columns which have no width specified (defaults to 100)
56976      * @type Number
56977      */
56978     this.defaultWidth = 100;
56979
56980     /**
56981      * Default sortable of columns which have no sortable specified (defaults to false)
56982      * @type Boolean
56983      */
56984     this.defaultSortable = false;
56985
56986     this.addEvents({
56987         /**
56988              * @event widthchange
56989              * Fires when the width of a column changes.
56990              * @param {ColumnModel} this
56991              * @param {Number} columnIndex The column index
56992              * @param {Number} newWidth The new width
56993              */
56994             "widthchange": true,
56995         /**
56996              * @event headerchange
56997              * Fires when the text of a header changes.
56998              * @param {ColumnModel} this
56999              * @param {Number} columnIndex The column index
57000              * @param {Number} newText The new header text
57001              */
57002             "headerchange": true,
57003         /**
57004              * @event hiddenchange
57005              * Fires when a column is hidden or "unhidden".
57006              * @param {ColumnModel} this
57007              * @param {Number} columnIndex The column index
57008              * @param {Boolean} hidden true if hidden, false otherwise
57009              */
57010             "hiddenchange": true,
57011             /**
57012          * @event columnmoved
57013          * Fires when a column is moved.
57014          * @param {ColumnModel} this
57015          * @param {Number} oldIndex
57016          * @param {Number} newIndex
57017          */
57018         "columnmoved" : true,
57019         /**
57020          * @event columlockchange
57021          * Fires when a column's locked state is changed
57022          * @param {ColumnModel} this
57023          * @param {Number} colIndex
57024          * @param {Boolean} locked true if locked
57025          */
57026         "columnlockchange" : true
57027     });
57028     Roo.grid.ColumnModel.superclass.constructor.call(this);
57029 };
57030 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57031     /**
57032      * @cfg {String} header The header text to display in the Grid view.
57033      */
57034     /**
57035      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57036      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57037      * specified, the column's index is used as an index into the Record's data Array.
57038      */
57039     /**
57040      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57041      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57042      */
57043     /**
57044      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57045      * Defaults to the value of the {@link #defaultSortable} property.
57046      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57047      */
57048     /**
57049      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57050      */
57051     /**
57052      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57053      */
57054     /**
57055      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57056      */
57057     /**
57058      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57059      */
57060     /**
57061      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57062      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57063      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57064      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57065      */
57066        /**
57067      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57068      */
57069     /**
57070      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57071      */
57072     /**
57073      * @cfg {String} cursor (Optional)
57074      */
57075     /**
57076      * @cfg {String} tooltip (Optional)
57077      */
57078     /**
57079      * @cfg {Number} xs (Optional)
57080      */
57081     /**
57082      * @cfg {Number} sm (Optional)
57083      */
57084     /**
57085      * @cfg {Number} md (Optional)
57086      */
57087     /**
57088      * @cfg {Number} lg (Optional)
57089      */
57090     /**
57091      * Returns the id of the column at the specified index.
57092      * @param {Number} index The column index
57093      * @return {String} the id
57094      */
57095     getColumnId : function(index){
57096         return this.config[index].id;
57097     },
57098
57099     /**
57100      * Returns the column for a specified id.
57101      * @param {String} id The column id
57102      * @return {Object} the column
57103      */
57104     getColumnById : function(id){
57105         return this.lookup[id];
57106     },
57107
57108     
57109     /**
57110      * Returns the column for a specified dataIndex.
57111      * @param {String} dataIndex The column dataIndex
57112      * @return {Object|Boolean} the column or false if not found
57113      */
57114     getColumnByDataIndex: function(dataIndex){
57115         var index = this.findColumnIndex(dataIndex);
57116         return index > -1 ? this.config[index] : false;
57117     },
57118     
57119     /**
57120      * Returns the index for a specified column id.
57121      * @param {String} id The column id
57122      * @return {Number} the index, or -1 if not found
57123      */
57124     getIndexById : function(id){
57125         for(var i = 0, len = this.config.length; i < len; i++){
57126             if(this.config[i].id == id){
57127                 return i;
57128             }
57129         }
57130         return -1;
57131     },
57132     
57133     /**
57134      * Returns the index for a specified column dataIndex.
57135      * @param {String} dataIndex The column dataIndex
57136      * @return {Number} the index, or -1 if not found
57137      */
57138     
57139     findColumnIndex : function(dataIndex){
57140         for(var i = 0, len = this.config.length; i < len; i++){
57141             if(this.config[i].dataIndex == dataIndex){
57142                 return i;
57143             }
57144         }
57145         return -1;
57146     },
57147     
57148     
57149     moveColumn : function(oldIndex, newIndex){
57150         var c = this.config[oldIndex];
57151         this.config.splice(oldIndex, 1);
57152         this.config.splice(newIndex, 0, c);
57153         this.dataMap = null;
57154         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57155     },
57156
57157     isLocked : function(colIndex){
57158         return this.config[colIndex].locked === true;
57159     },
57160
57161     setLocked : function(colIndex, value, suppressEvent){
57162         if(this.isLocked(colIndex) == value){
57163             return;
57164         }
57165         this.config[colIndex].locked = value;
57166         if(!suppressEvent){
57167             this.fireEvent("columnlockchange", this, colIndex, value);
57168         }
57169     },
57170
57171     getTotalLockedWidth : function(){
57172         var totalWidth = 0;
57173         for(var i = 0; i < this.config.length; i++){
57174             if(this.isLocked(i) && !this.isHidden(i)){
57175                 this.totalWidth += this.getColumnWidth(i);
57176             }
57177         }
57178         return totalWidth;
57179     },
57180
57181     getLockedCount : function(){
57182         for(var i = 0, len = this.config.length; i < len; i++){
57183             if(!this.isLocked(i)){
57184                 return i;
57185             }
57186         }
57187         
57188         return this.config.length;
57189     },
57190
57191     /**
57192      * Returns the number of columns.
57193      * @return {Number}
57194      */
57195     getColumnCount : function(visibleOnly){
57196         if(visibleOnly === true){
57197             var c = 0;
57198             for(var i = 0, len = this.config.length; i < len; i++){
57199                 if(!this.isHidden(i)){
57200                     c++;
57201                 }
57202             }
57203             return c;
57204         }
57205         return this.config.length;
57206     },
57207
57208     /**
57209      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57210      * @param {Function} fn
57211      * @param {Object} scope (optional)
57212      * @return {Array} result
57213      */
57214     getColumnsBy : function(fn, scope){
57215         var r = [];
57216         for(var i = 0, len = this.config.length; i < len; i++){
57217             var c = this.config[i];
57218             if(fn.call(scope||this, c, i) === true){
57219                 r[r.length] = c;
57220             }
57221         }
57222         return r;
57223     },
57224
57225     /**
57226      * Returns true if the specified column is sortable.
57227      * @param {Number} col The column index
57228      * @return {Boolean}
57229      */
57230     isSortable : function(col){
57231         if(typeof this.config[col].sortable == "undefined"){
57232             return this.defaultSortable;
57233         }
57234         return this.config[col].sortable;
57235     },
57236
57237     /**
57238      * Returns the rendering (formatting) function defined for the column.
57239      * @param {Number} col The column index.
57240      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57241      */
57242     getRenderer : function(col){
57243         if(!this.config[col].renderer){
57244             return Roo.grid.ColumnModel.defaultRenderer;
57245         }
57246         return this.config[col].renderer;
57247     },
57248
57249     /**
57250      * Sets the rendering (formatting) function for a column.
57251      * @param {Number} col The column index
57252      * @param {Function} fn The function to use to process the cell's raw data
57253      * to return HTML markup for the grid view. The render function is called with
57254      * the following parameters:<ul>
57255      * <li>Data value.</li>
57256      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57257      * <li>css A CSS style string to apply to the table cell.</li>
57258      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57259      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57260      * <li>Row index</li>
57261      * <li>Column index</li>
57262      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57263      */
57264     setRenderer : function(col, fn){
57265         this.config[col].renderer = fn;
57266     },
57267
57268     /**
57269      * Returns the width for the specified column.
57270      * @param {Number} col The column index
57271      * @return {Number}
57272      */
57273     getColumnWidth : function(col){
57274         return this.config[col].width * 1 || this.defaultWidth;
57275     },
57276
57277     /**
57278      * Sets the width for a column.
57279      * @param {Number} col The column index
57280      * @param {Number} width The new width
57281      */
57282     setColumnWidth : function(col, width, suppressEvent){
57283         this.config[col].width = width;
57284         this.totalWidth = null;
57285         if(!suppressEvent){
57286              this.fireEvent("widthchange", this, col, width);
57287         }
57288     },
57289
57290     /**
57291      * Returns the total width of all columns.
57292      * @param {Boolean} includeHidden True to include hidden column widths
57293      * @return {Number}
57294      */
57295     getTotalWidth : function(includeHidden){
57296         if(!this.totalWidth){
57297             this.totalWidth = 0;
57298             for(var i = 0, len = this.config.length; i < len; i++){
57299                 if(includeHidden || !this.isHidden(i)){
57300                     this.totalWidth += this.getColumnWidth(i);
57301                 }
57302             }
57303         }
57304         return this.totalWidth;
57305     },
57306
57307     /**
57308      * Returns the header for the specified column.
57309      * @param {Number} col The column index
57310      * @return {String}
57311      */
57312     getColumnHeader : function(col){
57313         return this.config[col].header;
57314     },
57315
57316     /**
57317      * Sets the header for a column.
57318      * @param {Number} col The column index
57319      * @param {String} header The new header
57320      */
57321     setColumnHeader : function(col, header){
57322         this.config[col].header = header;
57323         this.fireEvent("headerchange", this, col, header);
57324     },
57325
57326     /**
57327      * Returns the tooltip for the specified column.
57328      * @param {Number} col The column index
57329      * @return {String}
57330      */
57331     getColumnTooltip : function(col){
57332             return this.config[col].tooltip;
57333     },
57334     /**
57335      * Sets the tooltip for a column.
57336      * @param {Number} col The column index
57337      * @param {String} tooltip The new tooltip
57338      */
57339     setColumnTooltip : function(col, tooltip){
57340             this.config[col].tooltip = tooltip;
57341     },
57342
57343     /**
57344      * Returns the dataIndex for the specified column.
57345      * @param {Number} col The column index
57346      * @return {Number}
57347      */
57348     getDataIndex : function(col){
57349         return this.config[col].dataIndex;
57350     },
57351
57352     /**
57353      * Sets the dataIndex for a column.
57354      * @param {Number} col The column index
57355      * @param {Number} dataIndex The new dataIndex
57356      */
57357     setDataIndex : function(col, dataIndex){
57358         this.config[col].dataIndex = dataIndex;
57359     },
57360
57361     
57362     
57363     /**
57364      * Returns true if the cell is editable.
57365      * @param {Number} colIndex The column index
57366      * @param {Number} rowIndex The row index - this is nto actually used..?
57367      * @return {Boolean}
57368      */
57369     isCellEditable : function(colIndex, rowIndex){
57370         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57371     },
57372
57373     /**
57374      * Returns the editor defined for the cell/column.
57375      * return false or null to disable editing.
57376      * @param {Number} colIndex The column index
57377      * @param {Number} rowIndex The row index
57378      * @return {Object}
57379      */
57380     getCellEditor : function(colIndex, rowIndex){
57381         return this.config[colIndex].editor;
57382     },
57383
57384     /**
57385      * Sets if a column is editable.
57386      * @param {Number} col The column index
57387      * @param {Boolean} editable True if the column is editable
57388      */
57389     setEditable : function(col, editable){
57390         this.config[col].editable = editable;
57391     },
57392
57393
57394     /**
57395      * Returns true if the column is hidden.
57396      * @param {Number} colIndex The column index
57397      * @return {Boolean}
57398      */
57399     isHidden : function(colIndex){
57400         return this.config[colIndex].hidden;
57401     },
57402
57403
57404     /**
57405      * Returns true if the column width cannot be changed
57406      */
57407     isFixed : function(colIndex){
57408         return this.config[colIndex].fixed;
57409     },
57410
57411     /**
57412      * Returns true if the column can be resized
57413      * @return {Boolean}
57414      */
57415     isResizable : function(colIndex){
57416         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57417     },
57418     /**
57419      * Sets if a column is hidden.
57420      * @param {Number} colIndex The column index
57421      * @param {Boolean} hidden True if the column is hidden
57422      */
57423     setHidden : function(colIndex, hidden){
57424         this.config[colIndex].hidden = hidden;
57425         this.totalWidth = null;
57426         this.fireEvent("hiddenchange", this, colIndex, hidden);
57427     },
57428
57429     /**
57430      * Sets the editor for a column.
57431      * @param {Number} col The column index
57432      * @param {Object} editor The editor object
57433      */
57434     setEditor : function(col, editor){
57435         this.config[col].editor = editor;
57436     }
57437 });
57438
57439 Roo.grid.ColumnModel.defaultRenderer = function(value)
57440 {
57441     if(typeof value == "object") {
57442         return value;
57443     }
57444         if(typeof value == "string" && value.length < 1){
57445             return "&#160;";
57446         }
57447     
57448         return String.format("{0}", value);
57449 };
57450
57451 // Alias for backwards compatibility
57452 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57453 /*
57454  * Based on:
57455  * Ext JS Library 1.1.1
57456  * Copyright(c) 2006-2007, Ext JS, LLC.
57457  *
57458  * Originally Released Under LGPL - original licence link has changed is not relivant.
57459  *
57460  * Fork - LGPL
57461  * <script type="text/javascript">
57462  */
57463
57464 /**
57465  * @class Roo.grid.AbstractSelectionModel
57466  * @extends Roo.util.Observable
57467  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57468  * implemented by descendant classes.  This class should not be directly instantiated.
57469  * @constructor
57470  */
57471 Roo.grid.AbstractSelectionModel = function(){
57472     this.locked = false;
57473     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57474 };
57475
57476 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57477     /** @ignore Called by the grid automatically. Do not call directly. */
57478     init : function(grid){
57479         this.grid = grid;
57480         this.initEvents();
57481     },
57482
57483     /**
57484      * Locks the selections.
57485      */
57486     lock : function(){
57487         this.locked = true;
57488     },
57489
57490     /**
57491      * Unlocks the selections.
57492      */
57493     unlock : function(){
57494         this.locked = false;
57495     },
57496
57497     /**
57498      * Returns true if the selections are locked.
57499      * @return {Boolean}
57500      */
57501     isLocked : function(){
57502         return this.locked;
57503     }
57504 });/*
57505  * Based on:
57506  * Ext JS Library 1.1.1
57507  * Copyright(c) 2006-2007, Ext JS, LLC.
57508  *
57509  * Originally Released Under LGPL - original licence link has changed is not relivant.
57510  *
57511  * Fork - LGPL
57512  * <script type="text/javascript">
57513  */
57514 /**
57515  * @extends Roo.grid.AbstractSelectionModel
57516  * @class Roo.grid.RowSelectionModel
57517  * The default SelectionModel used by {@link Roo.grid.Grid}.
57518  * It supports multiple selections and keyboard selection/navigation. 
57519  * @constructor
57520  * @param {Object} config
57521  */
57522 Roo.grid.RowSelectionModel = function(config){
57523     Roo.apply(this, config);
57524     this.selections = new Roo.util.MixedCollection(false, function(o){
57525         return o.id;
57526     });
57527
57528     this.last = false;
57529     this.lastActive = false;
57530
57531     this.addEvents({
57532         /**
57533              * @event selectionchange
57534              * Fires when the selection changes
57535              * @param {SelectionModel} this
57536              */
57537             "selectionchange" : true,
57538         /**
57539              * @event afterselectionchange
57540              * Fires after the selection changes (eg. by key press or clicking)
57541              * @param {SelectionModel} this
57542              */
57543             "afterselectionchange" : true,
57544         /**
57545              * @event beforerowselect
57546              * Fires when a row is selected being selected, return false to cancel.
57547              * @param {SelectionModel} this
57548              * @param {Number} rowIndex The selected index
57549              * @param {Boolean} keepExisting False if other selections will be cleared
57550              */
57551             "beforerowselect" : true,
57552         /**
57553              * @event rowselect
57554              * Fires when a row is selected.
57555              * @param {SelectionModel} this
57556              * @param {Number} rowIndex The selected index
57557              * @param {Roo.data.Record} r The record
57558              */
57559             "rowselect" : true,
57560         /**
57561              * @event rowdeselect
57562              * Fires when a row is deselected.
57563              * @param {SelectionModel} this
57564              * @param {Number} rowIndex The selected index
57565              */
57566         "rowdeselect" : true
57567     });
57568     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57569     this.locked = false;
57570 };
57571
57572 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57573     /**
57574      * @cfg {Boolean} singleSelect
57575      * True to allow selection of only one row at a time (defaults to false)
57576      */
57577     singleSelect : false,
57578
57579     // private
57580     initEvents : function(){
57581
57582         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57583             this.grid.on("mousedown", this.handleMouseDown, this);
57584         }else{ // allow click to work like normal
57585             this.grid.on("rowclick", this.handleDragableRowClick, this);
57586         }
57587
57588         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57589             "up" : function(e){
57590                 if(!e.shiftKey){
57591                     this.selectPrevious(e.shiftKey);
57592                 }else if(this.last !== false && this.lastActive !== false){
57593                     var last = this.last;
57594                     this.selectRange(this.last,  this.lastActive-1);
57595                     this.grid.getView().focusRow(this.lastActive);
57596                     if(last !== false){
57597                         this.last = last;
57598                     }
57599                 }else{
57600                     this.selectFirstRow();
57601                 }
57602                 this.fireEvent("afterselectionchange", this);
57603             },
57604             "down" : function(e){
57605                 if(!e.shiftKey){
57606                     this.selectNext(e.shiftKey);
57607                 }else if(this.last !== false && this.lastActive !== false){
57608                     var last = this.last;
57609                     this.selectRange(this.last,  this.lastActive+1);
57610                     this.grid.getView().focusRow(this.lastActive);
57611                     if(last !== false){
57612                         this.last = last;
57613                     }
57614                 }else{
57615                     this.selectFirstRow();
57616                 }
57617                 this.fireEvent("afterselectionchange", this);
57618             },
57619             scope: this
57620         });
57621
57622         var view = this.grid.view;
57623         view.on("refresh", this.onRefresh, this);
57624         view.on("rowupdated", this.onRowUpdated, this);
57625         view.on("rowremoved", this.onRemove, this);
57626     },
57627
57628     // private
57629     onRefresh : function(){
57630         var ds = this.grid.dataSource, i, v = this.grid.view;
57631         var s = this.selections;
57632         s.each(function(r){
57633             if((i = ds.indexOfId(r.id)) != -1){
57634                 v.onRowSelect(i);
57635                 s.add(ds.getAt(i)); // updating the selection relate data
57636             }else{
57637                 s.remove(r);
57638             }
57639         });
57640     },
57641
57642     // private
57643     onRemove : function(v, index, r){
57644         this.selections.remove(r);
57645     },
57646
57647     // private
57648     onRowUpdated : function(v, index, r){
57649         if(this.isSelected(r)){
57650             v.onRowSelect(index);
57651         }
57652     },
57653
57654     /**
57655      * Select records.
57656      * @param {Array} records The records to select
57657      * @param {Boolean} keepExisting (optional) True to keep existing selections
57658      */
57659     selectRecords : function(records, keepExisting){
57660         if(!keepExisting){
57661             this.clearSelections();
57662         }
57663         var ds = this.grid.dataSource;
57664         for(var i = 0, len = records.length; i < len; i++){
57665             this.selectRow(ds.indexOf(records[i]), true);
57666         }
57667     },
57668
57669     /**
57670      * Gets the number of selected rows.
57671      * @return {Number}
57672      */
57673     getCount : function(){
57674         return this.selections.length;
57675     },
57676
57677     /**
57678      * Selects the first row in the grid.
57679      */
57680     selectFirstRow : function(){
57681         this.selectRow(0);
57682     },
57683
57684     /**
57685      * Select the last row.
57686      * @param {Boolean} keepExisting (optional) True to keep existing selections
57687      */
57688     selectLastRow : function(keepExisting){
57689         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57690     },
57691
57692     /**
57693      * Selects the row immediately following the last selected row.
57694      * @param {Boolean} keepExisting (optional) True to keep existing selections
57695      */
57696     selectNext : function(keepExisting){
57697         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57698             this.selectRow(this.last+1, keepExisting);
57699             this.grid.getView().focusRow(this.last);
57700         }
57701     },
57702
57703     /**
57704      * Selects the row that precedes the last selected row.
57705      * @param {Boolean} keepExisting (optional) True to keep existing selections
57706      */
57707     selectPrevious : function(keepExisting){
57708         if(this.last){
57709             this.selectRow(this.last-1, keepExisting);
57710             this.grid.getView().focusRow(this.last);
57711         }
57712     },
57713
57714     /**
57715      * Returns the selected records
57716      * @return {Array} Array of selected records
57717      */
57718     getSelections : function(){
57719         return [].concat(this.selections.items);
57720     },
57721
57722     /**
57723      * Returns the first selected record.
57724      * @return {Record}
57725      */
57726     getSelected : function(){
57727         return this.selections.itemAt(0);
57728     },
57729
57730
57731     /**
57732      * Clears all selections.
57733      */
57734     clearSelections : function(fast){
57735         if(this.locked) {
57736             return;
57737         }
57738         if(fast !== true){
57739             var ds = this.grid.dataSource;
57740             var s = this.selections;
57741             s.each(function(r){
57742                 this.deselectRow(ds.indexOfId(r.id));
57743             }, this);
57744             s.clear();
57745         }else{
57746             this.selections.clear();
57747         }
57748         this.last = false;
57749     },
57750
57751
57752     /**
57753      * Selects all rows.
57754      */
57755     selectAll : function(){
57756         if(this.locked) {
57757             return;
57758         }
57759         this.selections.clear();
57760         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57761             this.selectRow(i, true);
57762         }
57763     },
57764
57765     /**
57766      * Returns True if there is a selection.
57767      * @return {Boolean}
57768      */
57769     hasSelection : function(){
57770         return this.selections.length > 0;
57771     },
57772
57773     /**
57774      * Returns True if the specified row is selected.
57775      * @param {Number/Record} record The record or index of the record to check
57776      * @return {Boolean}
57777      */
57778     isSelected : function(index){
57779         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57780         return (r && this.selections.key(r.id) ? true : false);
57781     },
57782
57783     /**
57784      * Returns True if the specified record id is selected.
57785      * @param {String} id The id of record to check
57786      * @return {Boolean}
57787      */
57788     isIdSelected : function(id){
57789         return (this.selections.key(id) ? true : false);
57790     },
57791
57792     // private
57793     handleMouseDown : function(e, t){
57794         var view = this.grid.getView(), rowIndex;
57795         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57796             return;
57797         };
57798         if(e.shiftKey && this.last !== false){
57799             var last = this.last;
57800             this.selectRange(last, rowIndex, e.ctrlKey);
57801             this.last = last; // reset the last
57802             view.focusRow(rowIndex);
57803         }else{
57804             var isSelected = this.isSelected(rowIndex);
57805             if(e.button !== 0 && isSelected){
57806                 view.focusRow(rowIndex);
57807             }else if(e.ctrlKey && isSelected){
57808                 this.deselectRow(rowIndex);
57809             }else if(!isSelected){
57810                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57811                 view.focusRow(rowIndex);
57812             }
57813         }
57814         this.fireEvent("afterselectionchange", this);
57815     },
57816     // private
57817     handleDragableRowClick :  function(grid, rowIndex, e) 
57818     {
57819         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57820             this.selectRow(rowIndex, false);
57821             grid.view.focusRow(rowIndex);
57822              this.fireEvent("afterselectionchange", this);
57823         }
57824     },
57825     
57826     /**
57827      * Selects multiple rows.
57828      * @param {Array} rows Array of the indexes of the row to select
57829      * @param {Boolean} keepExisting (optional) True to keep existing selections
57830      */
57831     selectRows : function(rows, keepExisting){
57832         if(!keepExisting){
57833             this.clearSelections();
57834         }
57835         for(var i = 0, len = rows.length; i < len; i++){
57836             this.selectRow(rows[i], true);
57837         }
57838     },
57839
57840     /**
57841      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57842      * @param {Number} startRow The index of the first row in the range
57843      * @param {Number} endRow The index of the last row in the range
57844      * @param {Boolean} keepExisting (optional) True to retain existing selections
57845      */
57846     selectRange : function(startRow, endRow, keepExisting){
57847         if(this.locked) {
57848             return;
57849         }
57850         if(!keepExisting){
57851             this.clearSelections();
57852         }
57853         if(startRow <= endRow){
57854             for(var i = startRow; i <= endRow; i++){
57855                 this.selectRow(i, true);
57856             }
57857         }else{
57858             for(var i = startRow; i >= endRow; i--){
57859                 this.selectRow(i, true);
57860             }
57861         }
57862     },
57863
57864     /**
57865      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57866      * @param {Number} startRow The index of the first row in the range
57867      * @param {Number} endRow The index of the last row in the range
57868      */
57869     deselectRange : function(startRow, endRow, preventViewNotify){
57870         if(this.locked) {
57871             return;
57872         }
57873         for(var i = startRow; i <= endRow; i++){
57874             this.deselectRow(i, preventViewNotify);
57875         }
57876     },
57877
57878     /**
57879      * Selects a row.
57880      * @param {Number} row The index of the row to select
57881      * @param {Boolean} keepExisting (optional) True to keep existing selections
57882      */
57883     selectRow : function(index, keepExisting, preventViewNotify){
57884         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57885             return;
57886         }
57887         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57888             if(!keepExisting || this.singleSelect){
57889                 this.clearSelections();
57890             }
57891             var r = this.grid.dataSource.getAt(index);
57892             this.selections.add(r);
57893             this.last = this.lastActive = index;
57894             if(!preventViewNotify){
57895                 this.grid.getView().onRowSelect(index);
57896             }
57897             this.fireEvent("rowselect", this, index, r);
57898             this.fireEvent("selectionchange", this);
57899         }
57900     },
57901
57902     /**
57903      * Deselects a row.
57904      * @param {Number} row The index of the row to deselect
57905      */
57906     deselectRow : function(index, preventViewNotify){
57907         if(this.locked) {
57908             return;
57909         }
57910         if(this.last == index){
57911             this.last = false;
57912         }
57913         if(this.lastActive == index){
57914             this.lastActive = false;
57915         }
57916         var r = this.grid.dataSource.getAt(index);
57917         this.selections.remove(r);
57918         if(!preventViewNotify){
57919             this.grid.getView().onRowDeselect(index);
57920         }
57921         this.fireEvent("rowdeselect", this, index);
57922         this.fireEvent("selectionchange", this);
57923     },
57924
57925     // private
57926     restoreLast : function(){
57927         if(this._last){
57928             this.last = this._last;
57929         }
57930     },
57931
57932     // private
57933     acceptsNav : function(row, col, cm){
57934         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57935     },
57936
57937     // private
57938     onEditorKey : function(field, e){
57939         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57940         if(k == e.TAB){
57941             e.stopEvent();
57942             ed.completeEdit();
57943             if(e.shiftKey){
57944                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57945             }else{
57946                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57947             }
57948         }else if(k == e.ENTER && !e.ctrlKey){
57949             e.stopEvent();
57950             ed.completeEdit();
57951             if(e.shiftKey){
57952                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57953             }else{
57954                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57955             }
57956         }else if(k == e.ESC){
57957             ed.cancelEdit();
57958         }
57959         if(newCell){
57960             g.startEditing(newCell[0], newCell[1]);
57961         }
57962     }
57963 });/*
57964  * Based on:
57965  * Ext JS Library 1.1.1
57966  * Copyright(c) 2006-2007, Ext JS, LLC.
57967  *
57968  * Originally Released Under LGPL - original licence link has changed is not relivant.
57969  *
57970  * Fork - LGPL
57971  * <script type="text/javascript">
57972  */
57973 /**
57974  * @class Roo.grid.CellSelectionModel
57975  * @extends Roo.grid.AbstractSelectionModel
57976  * This class provides the basic implementation for cell selection in a grid.
57977  * @constructor
57978  * @param {Object} config The object containing the configuration of this model.
57979  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57980  */
57981 Roo.grid.CellSelectionModel = function(config){
57982     Roo.apply(this, config);
57983
57984     this.selection = null;
57985
57986     this.addEvents({
57987         /**
57988              * @event beforerowselect
57989              * Fires before a cell is selected.
57990              * @param {SelectionModel} this
57991              * @param {Number} rowIndex The selected row index
57992              * @param {Number} colIndex The selected cell index
57993              */
57994             "beforecellselect" : true,
57995         /**
57996              * @event cellselect
57997              * Fires when a cell is selected.
57998              * @param {SelectionModel} this
57999              * @param {Number} rowIndex The selected row index
58000              * @param {Number} colIndex The selected cell index
58001              */
58002             "cellselect" : true,
58003         /**
58004              * @event selectionchange
58005              * Fires when the active selection changes.
58006              * @param {SelectionModel} this
58007              * @param {Object} selection null for no selection or an object (o) with two properties
58008                 <ul>
58009                 <li>o.record: the record object for the row the selection is in</li>
58010                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58011                 </ul>
58012              */
58013             "selectionchange" : true,
58014         /**
58015              * @event tabend
58016              * Fires when the tab (or enter) was pressed on the last editable cell
58017              * You can use this to trigger add new row.
58018              * @param {SelectionModel} this
58019              */
58020             "tabend" : true,
58021          /**
58022              * @event beforeeditnext
58023              * Fires before the next editable sell is made active
58024              * You can use this to skip to another cell or fire the tabend
58025              *    if you set cell to false
58026              * @param {Object} eventdata object : { cell : [ row, col ] } 
58027              */
58028             "beforeeditnext" : true
58029     });
58030     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58031 };
58032
58033 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58034     
58035     enter_is_tab: false,
58036
58037     /** @ignore */
58038     initEvents : function(){
58039         this.grid.on("mousedown", this.handleMouseDown, this);
58040         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58041         var view = this.grid.view;
58042         view.on("refresh", this.onViewChange, this);
58043         view.on("rowupdated", this.onRowUpdated, this);
58044         view.on("beforerowremoved", this.clearSelections, this);
58045         view.on("beforerowsinserted", this.clearSelections, this);
58046         if(this.grid.isEditor){
58047             this.grid.on("beforeedit", this.beforeEdit,  this);
58048         }
58049     },
58050
58051         //private
58052     beforeEdit : function(e){
58053         this.select(e.row, e.column, false, true, e.record);
58054     },
58055
58056         //private
58057     onRowUpdated : function(v, index, r){
58058         if(this.selection && this.selection.record == r){
58059             v.onCellSelect(index, this.selection.cell[1]);
58060         }
58061     },
58062
58063         //private
58064     onViewChange : function(){
58065         this.clearSelections(true);
58066     },
58067
58068         /**
58069          * Returns the currently selected cell,.
58070          * @return {Array} The selected cell (row, column) or null if none selected.
58071          */
58072     getSelectedCell : function(){
58073         return this.selection ? this.selection.cell : null;
58074     },
58075
58076     /**
58077      * Clears all selections.
58078      * @param {Boolean} true to prevent the gridview from being notified about the change.
58079      */
58080     clearSelections : function(preventNotify){
58081         var s = this.selection;
58082         if(s){
58083             if(preventNotify !== true){
58084                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58085             }
58086             this.selection = null;
58087             this.fireEvent("selectionchange", this, null);
58088         }
58089     },
58090
58091     /**
58092      * Returns true if there is a selection.
58093      * @return {Boolean}
58094      */
58095     hasSelection : function(){
58096         return this.selection ? true : false;
58097     },
58098
58099     /** @ignore */
58100     handleMouseDown : function(e, t){
58101         var v = this.grid.getView();
58102         if(this.isLocked()){
58103             return;
58104         };
58105         var row = v.findRowIndex(t);
58106         var cell = v.findCellIndex(t);
58107         if(row !== false && cell !== false){
58108             this.select(row, cell);
58109         }
58110     },
58111
58112     /**
58113      * Selects a cell.
58114      * @param {Number} rowIndex
58115      * @param {Number} collIndex
58116      */
58117     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58118         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58119             this.clearSelections();
58120             r = r || this.grid.dataSource.getAt(rowIndex);
58121             this.selection = {
58122                 record : r,
58123                 cell : [rowIndex, colIndex]
58124             };
58125             if(!preventViewNotify){
58126                 var v = this.grid.getView();
58127                 v.onCellSelect(rowIndex, colIndex);
58128                 if(preventFocus !== true){
58129                     v.focusCell(rowIndex, colIndex);
58130                 }
58131             }
58132             this.fireEvent("cellselect", this, rowIndex, colIndex);
58133             this.fireEvent("selectionchange", this, this.selection);
58134         }
58135     },
58136
58137         //private
58138     isSelectable : function(rowIndex, colIndex, cm){
58139         return !cm.isHidden(colIndex);
58140     },
58141
58142     /** @ignore */
58143     handleKeyDown : function(e){
58144         //Roo.log('Cell Sel Model handleKeyDown');
58145         if(!e.isNavKeyPress()){
58146             return;
58147         }
58148         var g = this.grid, s = this.selection;
58149         if(!s){
58150             e.stopEvent();
58151             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58152             if(cell){
58153                 this.select(cell[0], cell[1]);
58154             }
58155             return;
58156         }
58157         var sm = this;
58158         var walk = function(row, col, step){
58159             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58160         };
58161         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58162         var newCell;
58163
58164       
58165
58166         switch(k){
58167             case e.TAB:
58168                 // handled by onEditorKey
58169                 if (g.isEditor && g.editing) {
58170                     return;
58171                 }
58172                 if(e.shiftKey) {
58173                     newCell = walk(r, c-1, -1);
58174                 } else {
58175                     newCell = walk(r, c+1, 1);
58176                 }
58177                 break;
58178             
58179             case e.DOWN:
58180                newCell = walk(r+1, c, 1);
58181                 break;
58182             
58183             case e.UP:
58184                 newCell = walk(r-1, c, -1);
58185                 break;
58186             
58187             case e.RIGHT:
58188                 newCell = walk(r, c+1, 1);
58189                 break;
58190             
58191             case e.LEFT:
58192                 newCell = walk(r, c-1, -1);
58193                 break;
58194             
58195             case e.ENTER:
58196                 
58197                 if(g.isEditor && !g.editing){
58198                    g.startEditing(r, c);
58199                    e.stopEvent();
58200                    return;
58201                 }
58202                 
58203                 
58204              break;
58205         };
58206         if(newCell){
58207             this.select(newCell[0], newCell[1]);
58208             e.stopEvent();
58209             
58210         }
58211     },
58212
58213     acceptsNav : function(row, col, cm){
58214         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58215     },
58216     /**
58217      * Selects a cell.
58218      * @param {Number} field (not used) - as it's normally used as a listener
58219      * @param {Number} e - event - fake it by using
58220      *
58221      * var e = Roo.EventObjectImpl.prototype;
58222      * e.keyCode = e.TAB
58223      *
58224      * 
58225      */
58226     onEditorKey : function(field, e){
58227         
58228         var k = e.getKey(),
58229             newCell,
58230             g = this.grid,
58231             ed = g.activeEditor,
58232             forward = false;
58233         ///Roo.log('onEditorKey' + k);
58234         
58235         
58236         if (this.enter_is_tab && k == e.ENTER) {
58237             k = e.TAB;
58238         }
58239         
58240         if(k == e.TAB){
58241             if(e.shiftKey){
58242                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58243             }else{
58244                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58245                 forward = true;
58246             }
58247             
58248             e.stopEvent();
58249             
58250         } else if(k == e.ENTER &&  !e.ctrlKey){
58251             ed.completeEdit();
58252             e.stopEvent();
58253             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58254         
58255                 } else if(k == e.ESC){
58256             ed.cancelEdit();
58257         }
58258                 
58259         if (newCell) {
58260             var ecall = { cell : newCell, forward : forward };
58261             this.fireEvent('beforeeditnext', ecall );
58262             newCell = ecall.cell;
58263                         forward = ecall.forward;
58264         }
58265                 
58266         if(newCell){
58267             //Roo.log('next cell after edit');
58268             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58269         } else if (forward) {
58270             // tabbed past last
58271             this.fireEvent.defer(100, this, ['tabend',this]);
58272         }
58273     }
58274 });/*
58275  * Based on:
58276  * Ext JS Library 1.1.1
58277  * Copyright(c) 2006-2007, Ext JS, LLC.
58278  *
58279  * Originally Released Under LGPL - original licence link has changed is not relivant.
58280  *
58281  * Fork - LGPL
58282  * <script type="text/javascript">
58283  */
58284  
58285 /**
58286  * @class Roo.grid.EditorGrid
58287  * @extends Roo.grid.Grid
58288  * Class for creating and editable grid.
58289  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58290  * The container MUST have some type of size defined for the grid to fill. The container will be 
58291  * automatically set to position relative if it isn't already.
58292  * @param {Object} dataSource The data model to bind to
58293  * @param {Object} colModel The column model with info about this grid's columns
58294  */
58295 Roo.grid.EditorGrid = function(container, config){
58296     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58297     this.getGridEl().addClass("xedit-grid");
58298
58299     if(!this.selModel){
58300         this.selModel = new Roo.grid.CellSelectionModel();
58301     }
58302
58303     this.activeEditor = null;
58304
58305         this.addEvents({
58306             /**
58307              * @event beforeedit
58308              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58309              * <ul style="padding:5px;padding-left:16px;">
58310              * <li>grid - This grid</li>
58311              * <li>record - The record being edited</li>
58312              * <li>field - The field name being edited</li>
58313              * <li>value - The value for the field being edited.</li>
58314              * <li>row - The grid row index</li>
58315              * <li>column - The grid column index</li>
58316              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58317              * </ul>
58318              * @param {Object} e An edit event (see above for description)
58319              */
58320             "beforeedit" : true,
58321             /**
58322              * @event afteredit
58323              * Fires after a cell is edited. <br />
58324              * <ul style="padding:5px;padding-left:16px;">
58325              * <li>grid - This grid</li>
58326              * <li>record - The record being edited</li>
58327              * <li>field - The field name being edited</li>
58328              * <li>value - The value being set</li>
58329              * <li>originalValue - The original value for the field, before the edit.</li>
58330              * <li>row - The grid row index</li>
58331              * <li>column - The grid column index</li>
58332              * </ul>
58333              * @param {Object} e An edit event (see above for description)
58334              */
58335             "afteredit" : true,
58336             /**
58337              * @event validateedit
58338              * Fires after a cell is edited, but before the value is set in the record. 
58339          * You can use this to modify the value being set in the field, Return false
58340              * to cancel the change. The edit event object has the following properties <br />
58341              * <ul style="padding:5px;padding-left:16px;">
58342          * <li>editor - This editor</li>
58343              * <li>grid - This grid</li>
58344              * <li>record - The record being edited</li>
58345              * <li>field - The field name being edited</li>
58346              * <li>value - The value being set</li>
58347              * <li>originalValue - The original value for the field, before the edit.</li>
58348              * <li>row - The grid row index</li>
58349              * <li>column - The grid column index</li>
58350              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58351              * </ul>
58352              * @param {Object} e An edit event (see above for description)
58353              */
58354             "validateedit" : true
58355         });
58356     this.on("bodyscroll", this.stopEditing,  this);
58357     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58358 };
58359
58360 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58361     /**
58362      * @cfg {Number} clicksToEdit
58363      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58364      */
58365     clicksToEdit: 2,
58366
58367     // private
58368     isEditor : true,
58369     // private
58370     trackMouseOver: false, // causes very odd FF errors
58371
58372     onCellDblClick : function(g, row, col){
58373         this.startEditing(row, col);
58374     },
58375
58376     onEditComplete : function(ed, value, startValue){
58377         this.editing = false;
58378         this.activeEditor = null;
58379         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58380         var r = ed.record;
58381         var field = this.colModel.getDataIndex(ed.col);
58382         var e = {
58383             grid: this,
58384             record: r,
58385             field: field,
58386             originalValue: startValue,
58387             value: value,
58388             row: ed.row,
58389             column: ed.col,
58390             cancel:false,
58391             editor: ed
58392         };
58393         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58394         cell.show();
58395           
58396         if(String(value) !== String(startValue)){
58397             
58398             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58399                 r.set(field, e.value);
58400                 // if we are dealing with a combo box..
58401                 // then we also set the 'name' colum to be the displayField
58402                 if (ed.field.displayField && ed.field.name) {
58403                     r.set(ed.field.name, ed.field.el.dom.value);
58404                 }
58405                 
58406                 delete e.cancel; //?? why!!!
58407                 this.fireEvent("afteredit", e);
58408             }
58409         } else {
58410             this.fireEvent("afteredit", e); // always fire it!
58411         }
58412         this.view.focusCell(ed.row, ed.col);
58413     },
58414
58415     /**
58416      * Starts editing the specified for the specified row/column
58417      * @param {Number} rowIndex
58418      * @param {Number} colIndex
58419      */
58420     startEditing : function(row, col){
58421         this.stopEditing();
58422         if(this.colModel.isCellEditable(col, row)){
58423             this.view.ensureVisible(row, col, true);
58424           
58425             var r = this.dataSource.getAt(row);
58426             var field = this.colModel.getDataIndex(col);
58427             var cell = Roo.get(this.view.getCell(row,col));
58428             var e = {
58429                 grid: this,
58430                 record: r,
58431                 field: field,
58432                 value: r.data[field],
58433                 row: row,
58434                 column: col,
58435                 cancel:false 
58436             };
58437             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58438                 this.editing = true;
58439                 var ed = this.colModel.getCellEditor(col, row);
58440                 
58441                 if (!ed) {
58442                     return;
58443                 }
58444                 if(!ed.rendered){
58445                     ed.render(ed.parentEl || document.body);
58446                 }
58447                 ed.field.reset();
58448                
58449                 cell.hide();
58450                 
58451                 (function(){ // complex but required for focus issues in safari, ie and opera
58452                     ed.row = row;
58453                     ed.col = col;
58454                     ed.record = r;
58455                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58456                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58457                     this.activeEditor = ed;
58458                     var v = r.data[field];
58459                     ed.startEdit(this.view.getCell(row, col), v);
58460                     // combo's with 'displayField and name set
58461                     if (ed.field.displayField && ed.field.name) {
58462                         ed.field.el.dom.value = r.data[ed.field.name];
58463                     }
58464                     
58465                     
58466                 }).defer(50, this);
58467             }
58468         }
58469     },
58470         
58471     /**
58472      * Stops any active editing
58473      */
58474     stopEditing : function(){
58475         if(this.activeEditor){
58476             this.activeEditor.completeEdit();
58477         }
58478         this.activeEditor = null;
58479     },
58480         
58481          /**
58482      * Called to get grid's drag proxy text, by default returns this.ddText.
58483      * @return {String}
58484      */
58485     getDragDropText : function(){
58486         var count = this.selModel.getSelectedCell() ? 1 : 0;
58487         return String.format(this.ddText, count, count == 1 ? '' : 's');
58488     }
58489         
58490 });/*
58491  * Based on:
58492  * Ext JS Library 1.1.1
58493  * Copyright(c) 2006-2007, Ext JS, LLC.
58494  *
58495  * Originally Released Under LGPL - original licence link has changed is not relivant.
58496  *
58497  * Fork - LGPL
58498  * <script type="text/javascript">
58499  */
58500
58501 // private - not really -- you end up using it !
58502 // This is a support class used internally by the Grid components
58503
58504 /**
58505  * @class Roo.grid.GridEditor
58506  * @extends Roo.Editor
58507  * Class for creating and editable grid elements.
58508  * @param {Object} config any settings (must include field)
58509  */
58510 Roo.grid.GridEditor = function(field, config){
58511     if (!config && field.field) {
58512         config = field;
58513         field = Roo.factory(config.field, Roo.form);
58514     }
58515     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58516     field.monitorTab = false;
58517 };
58518
58519 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58520     
58521     /**
58522      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58523      */
58524     
58525     alignment: "tl-tl",
58526     autoSize: "width",
58527     hideEl : false,
58528     cls: "x-small-editor x-grid-editor",
58529     shim:false,
58530     shadow:"frame"
58531 });/*
58532  * Based on:
58533  * Ext JS Library 1.1.1
58534  * Copyright(c) 2006-2007, Ext JS, LLC.
58535  *
58536  * Originally Released Under LGPL - original licence link has changed is not relivant.
58537  *
58538  * Fork - LGPL
58539  * <script type="text/javascript">
58540  */
58541   
58542
58543   
58544 Roo.grid.PropertyRecord = Roo.data.Record.create([
58545     {name:'name',type:'string'},  'value'
58546 ]);
58547
58548
58549 Roo.grid.PropertyStore = function(grid, source){
58550     this.grid = grid;
58551     this.store = new Roo.data.Store({
58552         recordType : Roo.grid.PropertyRecord
58553     });
58554     this.store.on('update', this.onUpdate,  this);
58555     if(source){
58556         this.setSource(source);
58557     }
58558     Roo.grid.PropertyStore.superclass.constructor.call(this);
58559 };
58560
58561
58562
58563 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58564     setSource : function(o){
58565         this.source = o;
58566         this.store.removeAll();
58567         var data = [];
58568         for(var k in o){
58569             if(this.isEditableValue(o[k])){
58570                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58571             }
58572         }
58573         this.store.loadRecords({records: data}, {}, true);
58574     },
58575
58576     onUpdate : function(ds, record, type){
58577         if(type == Roo.data.Record.EDIT){
58578             var v = record.data['value'];
58579             var oldValue = record.modified['value'];
58580             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58581                 this.source[record.id] = v;
58582                 record.commit();
58583                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58584             }else{
58585                 record.reject();
58586             }
58587         }
58588     },
58589
58590     getProperty : function(row){
58591        return this.store.getAt(row);
58592     },
58593
58594     isEditableValue: function(val){
58595         if(val && val instanceof Date){
58596             return true;
58597         }else if(typeof val == 'object' || typeof val == 'function'){
58598             return false;
58599         }
58600         return true;
58601     },
58602
58603     setValue : function(prop, value){
58604         this.source[prop] = value;
58605         this.store.getById(prop).set('value', value);
58606     },
58607
58608     getSource : function(){
58609         return this.source;
58610     }
58611 });
58612
58613 Roo.grid.PropertyColumnModel = function(grid, store){
58614     this.grid = grid;
58615     var g = Roo.grid;
58616     g.PropertyColumnModel.superclass.constructor.call(this, [
58617         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58618         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58619     ]);
58620     this.store = store;
58621     this.bselect = Roo.DomHelper.append(document.body, {
58622         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58623             {tag: 'option', value: 'true', html: 'true'},
58624             {tag: 'option', value: 'false', html: 'false'}
58625         ]
58626     });
58627     Roo.id(this.bselect);
58628     var f = Roo.form;
58629     this.editors = {
58630         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58631         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58632         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58633         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58634         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58635     };
58636     this.renderCellDelegate = this.renderCell.createDelegate(this);
58637     this.renderPropDelegate = this.renderProp.createDelegate(this);
58638 };
58639
58640 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58641     
58642     
58643     nameText : 'Name',
58644     valueText : 'Value',
58645     
58646     dateFormat : 'm/j/Y',
58647     
58648     
58649     renderDate : function(dateVal){
58650         return dateVal.dateFormat(this.dateFormat);
58651     },
58652
58653     renderBool : function(bVal){
58654         return bVal ? 'true' : 'false';
58655     },
58656
58657     isCellEditable : function(colIndex, rowIndex){
58658         return colIndex == 1;
58659     },
58660
58661     getRenderer : function(col){
58662         return col == 1 ?
58663             this.renderCellDelegate : this.renderPropDelegate;
58664     },
58665
58666     renderProp : function(v){
58667         return this.getPropertyName(v);
58668     },
58669
58670     renderCell : function(val){
58671         var rv = val;
58672         if(val instanceof Date){
58673             rv = this.renderDate(val);
58674         }else if(typeof val == 'boolean'){
58675             rv = this.renderBool(val);
58676         }
58677         return Roo.util.Format.htmlEncode(rv);
58678     },
58679
58680     getPropertyName : function(name){
58681         var pn = this.grid.propertyNames;
58682         return pn && pn[name] ? pn[name] : name;
58683     },
58684
58685     getCellEditor : function(colIndex, rowIndex){
58686         var p = this.store.getProperty(rowIndex);
58687         var n = p.data['name'], val = p.data['value'];
58688         
58689         if(typeof(this.grid.customEditors[n]) == 'string'){
58690             return this.editors[this.grid.customEditors[n]];
58691         }
58692         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58693             return this.grid.customEditors[n];
58694         }
58695         if(val instanceof Date){
58696             return this.editors['date'];
58697         }else if(typeof val == 'number'){
58698             return this.editors['number'];
58699         }else if(typeof val == 'boolean'){
58700             return this.editors['boolean'];
58701         }else{
58702             return this.editors['string'];
58703         }
58704     }
58705 });
58706
58707 /**
58708  * @class Roo.grid.PropertyGrid
58709  * @extends Roo.grid.EditorGrid
58710  * This class represents the  interface of a component based property grid control.
58711  * <br><br>Usage:<pre><code>
58712  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58713       
58714  });
58715  // set any options
58716  grid.render();
58717  * </code></pre>
58718   
58719  * @constructor
58720  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58721  * The container MUST have some type of size defined for the grid to fill. The container will be
58722  * automatically set to position relative if it isn't already.
58723  * @param {Object} config A config object that sets properties on this grid.
58724  */
58725 Roo.grid.PropertyGrid = function(container, config){
58726     config = config || {};
58727     var store = new Roo.grid.PropertyStore(this);
58728     this.store = store;
58729     var cm = new Roo.grid.PropertyColumnModel(this, store);
58730     store.store.sort('name', 'ASC');
58731     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58732         ds: store.store,
58733         cm: cm,
58734         enableColLock:false,
58735         enableColumnMove:false,
58736         stripeRows:false,
58737         trackMouseOver: false,
58738         clicksToEdit:1
58739     }, config));
58740     this.getGridEl().addClass('x-props-grid');
58741     this.lastEditRow = null;
58742     this.on('columnresize', this.onColumnResize, this);
58743     this.addEvents({
58744          /**
58745              * @event beforepropertychange
58746              * Fires before a property changes (return false to stop?)
58747              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58748              * @param {String} id Record Id
58749              * @param {String} newval New Value
58750          * @param {String} oldval Old Value
58751              */
58752         "beforepropertychange": true,
58753         /**
58754              * @event propertychange
58755              * Fires after a property changes
58756              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58757              * @param {String} id Record Id
58758              * @param {String} newval New Value
58759          * @param {String} oldval Old Value
58760              */
58761         "propertychange": true
58762     });
58763     this.customEditors = this.customEditors || {};
58764 };
58765 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58766     
58767      /**
58768      * @cfg {Object} customEditors map of colnames=> custom editors.
58769      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58770      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58771      * false disables editing of the field.
58772          */
58773     
58774       /**
58775      * @cfg {Object} propertyNames map of property Names to their displayed value
58776          */
58777     
58778     render : function(){
58779         Roo.grid.PropertyGrid.superclass.render.call(this);
58780         this.autoSize.defer(100, this);
58781     },
58782
58783     autoSize : function(){
58784         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58785         if(this.view){
58786             this.view.fitColumns();
58787         }
58788     },
58789
58790     onColumnResize : function(){
58791         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58792         this.autoSize();
58793     },
58794     /**
58795      * Sets the data for the Grid
58796      * accepts a Key => Value object of all the elements avaiable.
58797      * @param {Object} data  to appear in grid.
58798      */
58799     setSource : function(source){
58800         this.store.setSource(source);
58801         //this.autoSize();
58802     },
58803     /**
58804      * Gets all the data from the grid.
58805      * @return {Object} data  data stored in grid
58806      */
58807     getSource : function(){
58808         return this.store.getSource();
58809     }
58810 });/*
58811   
58812  * Licence LGPL
58813  
58814  */
58815  
58816 /**
58817  * @class Roo.grid.Calendar
58818  * @extends Roo.util.Grid
58819  * This class extends the Grid to provide a calendar widget
58820  * <br><br>Usage:<pre><code>
58821  var grid = new Roo.grid.Calendar("my-container-id", {
58822      ds: myDataStore,
58823      cm: myColModel,
58824      selModel: mySelectionModel,
58825      autoSizeColumns: true,
58826      monitorWindowResize: false,
58827      trackMouseOver: true
58828      eventstore : real data store..
58829  });
58830  // set any options
58831  grid.render();
58832   
58833   * @constructor
58834  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58835  * The container MUST have some type of size defined for the grid to fill. The container will be
58836  * automatically set to position relative if it isn't already.
58837  * @param {Object} config A config object that sets properties on this grid.
58838  */
58839 Roo.grid.Calendar = function(container, config){
58840         // initialize the container
58841         this.container = Roo.get(container);
58842         this.container.update("");
58843         this.container.setStyle("overflow", "hidden");
58844     this.container.addClass('x-grid-container');
58845
58846     this.id = this.container.id;
58847
58848     Roo.apply(this, config);
58849     // check and correct shorthanded configs
58850     
58851     var rows = [];
58852     var d =1;
58853     for (var r = 0;r < 6;r++) {
58854         
58855         rows[r]=[];
58856         for (var c =0;c < 7;c++) {
58857             rows[r][c]= '';
58858         }
58859     }
58860     if (this.eventStore) {
58861         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58862         this.eventStore.on('load',this.onLoad, this);
58863         this.eventStore.on('beforeload',this.clearEvents, this);
58864          
58865     }
58866     
58867     this.dataSource = new Roo.data.Store({
58868             proxy: new Roo.data.MemoryProxy(rows),
58869             reader: new Roo.data.ArrayReader({}, [
58870                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58871     });
58872
58873     this.dataSource.load();
58874     this.ds = this.dataSource;
58875     this.ds.xmodule = this.xmodule || false;
58876     
58877     
58878     var cellRender = function(v,x,r)
58879     {
58880         return String.format(
58881             '<div class="fc-day  fc-widget-content"><div>' +
58882                 '<div class="fc-event-container"></div>' +
58883                 '<div class="fc-day-number">{0}</div>'+
58884                 
58885                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58886             '</div></div>', v);
58887     
58888     }
58889     
58890     
58891     this.colModel = new Roo.grid.ColumnModel( [
58892         {
58893             xtype: 'ColumnModel',
58894             xns: Roo.grid,
58895             dataIndex : 'weekday0',
58896             header : 'Sunday',
58897             renderer : cellRender
58898         },
58899         {
58900             xtype: 'ColumnModel',
58901             xns: Roo.grid,
58902             dataIndex : 'weekday1',
58903             header : 'Monday',
58904             renderer : cellRender
58905         },
58906         {
58907             xtype: 'ColumnModel',
58908             xns: Roo.grid,
58909             dataIndex : 'weekday2',
58910             header : 'Tuesday',
58911             renderer : cellRender
58912         },
58913         {
58914             xtype: 'ColumnModel',
58915             xns: Roo.grid,
58916             dataIndex : 'weekday3',
58917             header : 'Wednesday',
58918             renderer : cellRender
58919         },
58920         {
58921             xtype: 'ColumnModel',
58922             xns: Roo.grid,
58923             dataIndex : 'weekday4',
58924             header : 'Thursday',
58925             renderer : cellRender
58926         },
58927         {
58928             xtype: 'ColumnModel',
58929             xns: Roo.grid,
58930             dataIndex : 'weekday5',
58931             header : 'Friday',
58932             renderer : cellRender
58933         },
58934         {
58935             xtype: 'ColumnModel',
58936             xns: Roo.grid,
58937             dataIndex : 'weekday6',
58938             header : 'Saturday',
58939             renderer : cellRender
58940         }
58941     ]);
58942     this.cm = this.colModel;
58943     this.cm.xmodule = this.xmodule || false;
58944  
58945         
58946           
58947     //this.selModel = new Roo.grid.CellSelectionModel();
58948     //this.sm = this.selModel;
58949     //this.selModel.init(this);
58950     
58951     
58952     if(this.width){
58953         this.container.setWidth(this.width);
58954     }
58955
58956     if(this.height){
58957         this.container.setHeight(this.height);
58958     }
58959     /** @private */
58960         this.addEvents({
58961         // raw events
58962         /**
58963          * @event click
58964          * The raw click event for the entire grid.
58965          * @param {Roo.EventObject} e
58966          */
58967         "click" : true,
58968         /**
58969          * @event dblclick
58970          * The raw dblclick event for the entire grid.
58971          * @param {Roo.EventObject} e
58972          */
58973         "dblclick" : true,
58974         /**
58975          * @event contextmenu
58976          * The raw contextmenu event for the entire grid.
58977          * @param {Roo.EventObject} e
58978          */
58979         "contextmenu" : true,
58980         /**
58981          * @event mousedown
58982          * The raw mousedown event for the entire grid.
58983          * @param {Roo.EventObject} e
58984          */
58985         "mousedown" : true,
58986         /**
58987          * @event mouseup
58988          * The raw mouseup event for the entire grid.
58989          * @param {Roo.EventObject} e
58990          */
58991         "mouseup" : true,
58992         /**
58993          * @event mouseover
58994          * The raw mouseover event for the entire grid.
58995          * @param {Roo.EventObject} e
58996          */
58997         "mouseover" : true,
58998         /**
58999          * @event mouseout
59000          * The raw mouseout event for the entire grid.
59001          * @param {Roo.EventObject} e
59002          */
59003         "mouseout" : true,
59004         /**
59005          * @event keypress
59006          * The raw keypress event for the entire grid.
59007          * @param {Roo.EventObject} e
59008          */
59009         "keypress" : true,
59010         /**
59011          * @event keydown
59012          * The raw keydown event for the entire grid.
59013          * @param {Roo.EventObject} e
59014          */
59015         "keydown" : true,
59016
59017         // custom events
59018
59019         /**
59020          * @event cellclick
59021          * Fires when a cell is clicked
59022          * @param {Grid} this
59023          * @param {Number} rowIndex
59024          * @param {Number} columnIndex
59025          * @param {Roo.EventObject} e
59026          */
59027         "cellclick" : true,
59028         /**
59029          * @event celldblclick
59030          * Fires when a cell is double clicked
59031          * @param {Grid} this
59032          * @param {Number} rowIndex
59033          * @param {Number} columnIndex
59034          * @param {Roo.EventObject} e
59035          */
59036         "celldblclick" : true,
59037         /**
59038          * @event rowclick
59039          * Fires when a row is clicked
59040          * @param {Grid} this
59041          * @param {Number} rowIndex
59042          * @param {Roo.EventObject} e
59043          */
59044         "rowclick" : true,
59045         /**
59046          * @event rowdblclick
59047          * Fires when a row is double clicked
59048          * @param {Grid} this
59049          * @param {Number} rowIndex
59050          * @param {Roo.EventObject} e
59051          */
59052         "rowdblclick" : true,
59053         /**
59054          * @event headerclick
59055          * Fires when a header is clicked
59056          * @param {Grid} this
59057          * @param {Number} columnIndex
59058          * @param {Roo.EventObject} e
59059          */
59060         "headerclick" : true,
59061         /**
59062          * @event headerdblclick
59063          * Fires when a header cell is double clicked
59064          * @param {Grid} this
59065          * @param {Number} columnIndex
59066          * @param {Roo.EventObject} e
59067          */
59068         "headerdblclick" : true,
59069         /**
59070          * @event rowcontextmenu
59071          * Fires when a row is right clicked
59072          * @param {Grid} this
59073          * @param {Number} rowIndex
59074          * @param {Roo.EventObject} e
59075          */
59076         "rowcontextmenu" : true,
59077         /**
59078          * @event cellcontextmenu
59079          * Fires when a cell is right clicked
59080          * @param {Grid} this
59081          * @param {Number} rowIndex
59082          * @param {Number} cellIndex
59083          * @param {Roo.EventObject} e
59084          */
59085          "cellcontextmenu" : true,
59086         /**
59087          * @event headercontextmenu
59088          * Fires when a header is right clicked
59089          * @param {Grid} this
59090          * @param {Number} columnIndex
59091          * @param {Roo.EventObject} e
59092          */
59093         "headercontextmenu" : true,
59094         /**
59095          * @event bodyscroll
59096          * Fires when the body element is scrolled
59097          * @param {Number} scrollLeft
59098          * @param {Number} scrollTop
59099          */
59100         "bodyscroll" : true,
59101         /**
59102          * @event columnresize
59103          * Fires when the user resizes a column
59104          * @param {Number} columnIndex
59105          * @param {Number} newSize
59106          */
59107         "columnresize" : true,
59108         /**
59109          * @event columnmove
59110          * Fires when the user moves a column
59111          * @param {Number} oldIndex
59112          * @param {Number} newIndex
59113          */
59114         "columnmove" : true,
59115         /**
59116          * @event startdrag
59117          * Fires when row(s) start being dragged
59118          * @param {Grid} this
59119          * @param {Roo.GridDD} dd The drag drop object
59120          * @param {event} e The raw browser event
59121          */
59122         "startdrag" : true,
59123         /**
59124          * @event enddrag
59125          * Fires when a drag operation is complete
59126          * @param {Grid} this
59127          * @param {Roo.GridDD} dd The drag drop object
59128          * @param {event} e The raw browser event
59129          */
59130         "enddrag" : true,
59131         /**
59132          * @event dragdrop
59133          * Fires when dragged row(s) are dropped on a valid DD target
59134          * @param {Grid} this
59135          * @param {Roo.GridDD} dd The drag drop object
59136          * @param {String} targetId The target drag drop object
59137          * @param {event} e The raw browser event
59138          */
59139         "dragdrop" : true,
59140         /**
59141          * @event dragover
59142          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59143          * @param {Grid} this
59144          * @param {Roo.GridDD} dd The drag drop object
59145          * @param {String} targetId The target drag drop object
59146          * @param {event} e The raw browser event
59147          */
59148         "dragover" : true,
59149         /**
59150          * @event dragenter
59151          *  Fires when the dragged row(s) first cross another DD target while being dragged
59152          * @param {Grid} this
59153          * @param {Roo.GridDD} dd The drag drop object
59154          * @param {String} targetId The target drag drop object
59155          * @param {event} e The raw browser event
59156          */
59157         "dragenter" : true,
59158         /**
59159          * @event dragout
59160          * Fires when the dragged row(s) leave another DD target while being dragged
59161          * @param {Grid} this
59162          * @param {Roo.GridDD} dd The drag drop object
59163          * @param {String} targetId The target drag drop object
59164          * @param {event} e The raw browser event
59165          */
59166         "dragout" : true,
59167         /**
59168          * @event rowclass
59169          * Fires when a row is rendered, so you can change add a style to it.
59170          * @param {GridView} gridview   The grid view
59171          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59172          */
59173         'rowclass' : true,
59174
59175         /**
59176          * @event render
59177          * Fires when the grid is rendered
59178          * @param {Grid} grid
59179          */
59180         'render' : true,
59181             /**
59182              * @event select
59183              * Fires when a date is selected
59184              * @param {DatePicker} this
59185              * @param {Date} date The selected date
59186              */
59187         'select': true,
59188         /**
59189              * @event monthchange
59190              * Fires when the displayed month changes 
59191              * @param {DatePicker} this
59192              * @param {Date} date The selected month
59193              */
59194         'monthchange': true,
59195         /**
59196              * @event evententer
59197              * Fires when mouse over an event
59198              * @param {Calendar} this
59199              * @param {event} Event
59200              */
59201         'evententer': true,
59202         /**
59203              * @event eventleave
59204              * Fires when the mouse leaves an
59205              * @param {Calendar} this
59206              * @param {event}
59207              */
59208         'eventleave': true,
59209         /**
59210              * @event eventclick
59211              * Fires when the mouse click an
59212              * @param {Calendar} this
59213              * @param {event}
59214              */
59215         'eventclick': true,
59216         /**
59217              * @event eventrender
59218              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59219              * @param {Calendar} this
59220              * @param {data} data to be modified
59221              */
59222         'eventrender': true
59223         
59224     });
59225
59226     Roo.grid.Grid.superclass.constructor.call(this);
59227     this.on('render', function() {
59228         this.view.el.addClass('x-grid-cal'); 
59229         
59230         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59231
59232     },this);
59233     
59234     if (!Roo.grid.Calendar.style) {
59235         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59236             
59237             
59238             '.x-grid-cal .x-grid-col' :  {
59239                 height: 'auto !important',
59240                 'vertical-align': 'top'
59241             },
59242             '.x-grid-cal  .fc-event-hori' : {
59243                 height: '14px'
59244             }
59245              
59246             
59247         }, Roo.id());
59248     }
59249
59250     
59251     
59252 };
59253 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59254     /**
59255      * @cfg {Store} eventStore The store that loads events.
59256      */
59257     eventStore : 25,
59258
59259      
59260     activeDate : false,
59261     startDay : 0,
59262     autoWidth : true,
59263     monitorWindowResize : false,
59264
59265     
59266     resizeColumns : function() {
59267         var col = (this.view.el.getWidth() / 7) - 3;
59268         // loop through cols, and setWidth
59269         for(var i =0 ; i < 7 ; i++){
59270             this.cm.setColumnWidth(i, col);
59271         }
59272     },
59273      setDate :function(date) {
59274         
59275         Roo.log('setDate?');
59276         
59277         this.resizeColumns();
59278         var vd = this.activeDate;
59279         this.activeDate = date;
59280 //        if(vd && this.el){
59281 //            var t = date.getTime();
59282 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59283 //                Roo.log('using add remove');
59284 //                
59285 //                this.fireEvent('monthchange', this, date);
59286 //                
59287 //                this.cells.removeClass("fc-state-highlight");
59288 //                this.cells.each(function(c){
59289 //                   if(c.dateValue == t){
59290 //                       c.addClass("fc-state-highlight");
59291 //                       setTimeout(function(){
59292 //                            try{c.dom.firstChild.focus();}catch(e){}
59293 //                       }, 50);
59294 //                       return false;
59295 //                   }
59296 //                   return true;
59297 //                });
59298 //                return;
59299 //            }
59300 //        }
59301         
59302         var days = date.getDaysInMonth();
59303         
59304         var firstOfMonth = date.getFirstDateOfMonth();
59305         var startingPos = firstOfMonth.getDay()-this.startDay;
59306         
59307         if(startingPos < this.startDay){
59308             startingPos += 7;
59309         }
59310         
59311         var pm = date.add(Date.MONTH, -1);
59312         var prevStart = pm.getDaysInMonth()-startingPos;
59313 //        
59314         
59315         
59316         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59317         
59318         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59319         //this.cells.addClassOnOver('fc-state-hover');
59320         
59321         var cells = this.cells.elements;
59322         var textEls = this.textNodes;
59323         
59324         //Roo.each(cells, function(cell){
59325         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59326         //});
59327         
59328         days += startingPos;
59329
59330         // convert everything to numbers so it's fast
59331         var day = 86400000;
59332         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59333         //Roo.log(d);
59334         //Roo.log(pm);
59335         //Roo.log(prevStart);
59336         
59337         var today = new Date().clearTime().getTime();
59338         var sel = date.clearTime().getTime();
59339         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59340         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59341         var ddMatch = this.disabledDatesRE;
59342         var ddText = this.disabledDatesText;
59343         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59344         var ddaysText = this.disabledDaysText;
59345         var format = this.format;
59346         
59347         var setCellClass = function(cal, cell){
59348             
59349             //Roo.log('set Cell Class');
59350             cell.title = "";
59351             var t = d.getTime();
59352             
59353             //Roo.log(d);
59354             
59355             
59356             cell.dateValue = t;
59357             if(t == today){
59358                 cell.className += " fc-today";
59359                 cell.className += " fc-state-highlight";
59360                 cell.title = cal.todayText;
59361             }
59362             if(t == sel){
59363                 // disable highlight in other month..
59364                 cell.className += " fc-state-highlight";
59365                 
59366             }
59367             // disabling
59368             if(t < min) {
59369                 //cell.className = " fc-state-disabled";
59370                 cell.title = cal.minText;
59371                 return;
59372             }
59373             if(t > max) {
59374                 //cell.className = " fc-state-disabled";
59375                 cell.title = cal.maxText;
59376                 return;
59377             }
59378             if(ddays){
59379                 if(ddays.indexOf(d.getDay()) != -1){
59380                     // cell.title = ddaysText;
59381                    // cell.className = " fc-state-disabled";
59382                 }
59383             }
59384             if(ddMatch && format){
59385                 var fvalue = d.dateFormat(format);
59386                 if(ddMatch.test(fvalue)){
59387                     cell.title = ddText.replace("%0", fvalue);
59388                    cell.className = " fc-state-disabled";
59389                 }
59390             }
59391             
59392             if (!cell.initialClassName) {
59393                 cell.initialClassName = cell.dom.className;
59394             }
59395             
59396             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59397         };
59398
59399         var i = 0;
59400         
59401         for(; i < startingPos; i++) {
59402             cells[i].dayName =  (++prevStart);
59403             Roo.log(textEls[i]);
59404             d.setDate(d.getDate()+1);
59405             
59406             //cells[i].className = "fc-past fc-other-month";
59407             setCellClass(this, cells[i]);
59408         }
59409         
59410         var intDay = 0;
59411         
59412         for(; i < days; i++){
59413             intDay = i - startingPos + 1;
59414             cells[i].dayName =  (intDay);
59415             d.setDate(d.getDate()+1);
59416             
59417             cells[i].className = ''; // "x-date-active";
59418             setCellClass(this, cells[i]);
59419         }
59420         var extraDays = 0;
59421         
59422         for(; i < 42; i++) {
59423             //textEls[i].innerHTML = (++extraDays);
59424             
59425             d.setDate(d.getDate()+1);
59426             cells[i].dayName = (++extraDays);
59427             cells[i].className = "fc-future fc-other-month";
59428             setCellClass(this, cells[i]);
59429         }
59430         
59431         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59432         
59433         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59434         
59435         // this will cause all the cells to mis
59436         var rows= [];
59437         var i =0;
59438         for (var r = 0;r < 6;r++) {
59439             for (var c =0;c < 7;c++) {
59440                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59441             }    
59442         }
59443         
59444         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59445         for(i=0;i<cells.length;i++) {
59446             
59447             this.cells.elements[i].dayName = cells[i].dayName ;
59448             this.cells.elements[i].className = cells[i].className;
59449             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59450             this.cells.elements[i].title = cells[i].title ;
59451             this.cells.elements[i].dateValue = cells[i].dateValue ;
59452         }
59453         
59454         
59455         
59456         
59457         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59458         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59459         
59460         ////if(totalRows != 6){
59461             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59462            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59463        // }
59464         
59465         this.fireEvent('monthchange', this, date);
59466         
59467         
59468     },
59469  /**
59470      * Returns the grid's SelectionModel.
59471      * @return {SelectionModel}
59472      */
59473     getSelectionModel : function(){
59474         if(!this.selModel){
59475             this.selModel = new Roo.grid.CellSelectionModel();
59476         }
59477         return this.selModel;
59478     },
59479
59480     load: function() {
59481         this.eventStore.load()
59482         
59483         
59484         
59485     },
59486     
59487     findCell : function(dt) {
59488         dt = dt.clearTime().getTime();
59489         var ret = false;
59490         this.cells.each(function(c){
59491             //Roo.log("check " +c.dateValue + '?=' + dt);
59492             if(c.dateValue == dt){
59493                 ret = c;
59494                 return false;
59495             }
59496             return true;
59497         });
59498         
59499         return ret;
59500     },
59501     
59502     findCells : function(rec) {
59503         var s = rec.data.start_dt.clone().clearTime().getTime();
59504        // Roo.log(s);
59505         var e= rec.data.end_dt.clone().clearTime().getTime();
59506        // Roo.log(e);
59507         var ret = [];
59508         this.cells.each(function(c){
59509              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59510             
59511             if(c.dateValue > e){
59512                 return ;
59513             }
59514             if(c.dateValue < s){
59515                 return ;
59516             }
59517             ret.push(c);
59518         });
59519         
59520         return ret;    
59521     },
59522     
59523     findBestRow: function(cells)
59524     {
59525         var ret = 0;
59526         
59527         for (var i =0 ; i < cells.length;i++) {
59528             ret  = Math.max(cells[i].rows || 0,ret);
59529         }
59530         return ret;
59531         
59532     },
59533     
59534     
59535     addItem : function(rec)
59536     {
59537         // look for vertical location slot in
59538         var cells = this.findCells(rec);
59539         
59540         rec.row = this.findBestRow(cells);
59541         
59542         // work out the location.
59543         
59544         var crow = false;
59545         var rows = [];
59546         for(var i =0; i < cells.length; i++) {
59547             if (!crow) {
59548                 crow = {
59549                     start : cells[i],
59550                     end :  cells[i]
59551                 };
59552                 continue;
59553             }
59554             if (crow.start.getY() == cells[i].getY()) {
59555                 // on same row.
59556                 crow.end = cells[i];
59557                 continue;
59558             }
59559             // different row.
59560             rows.push(crow);
59561             crow = {
59562                 start: cells[i],
59563                 end : cells[i]
59564             };
59565             
59566         }
59567         
59568         rows.push(crow);
59569         rec.els = [];
59570         rec.rows = rows;
59571         rec.cells = cells;
59572         for (var i = 0; i < cells.length;i++) {
59573             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59574             
59575         }
59576         
59577         
59578     },
59579     
59580     clearEvents: function() {
59581         
59582         if (!this.eventStore.getCount()) {
59583             return;
59584         }
59585         // reset number of rows in cells.
59586         Roo.each(this.cells.elements, function(c){
59587             c.rows = 0;
59588         });
59589         
59590         this.eventStore.each(function(e) {
59591             this.clearEvent(e);
59592         },this);
59593         
59594     },
59595     
59596     clearEvent : function(ev)
59597     {
59598         if (ev.els) {
59599             Roo.each(ev.els, function(el) {
59600                 el.un('mouseenter' ,this.onEventEnter, this);
59601                 el.un('mouseleave' ,this.onEventLeave, this);
59602                 el.remove();
59603             },this);
59604             ev.els = [];
59605         }
59606     },
59607     
59608     
59609     renderEvent : function(ev,ctr) {
59610         if (!ctr) {
59611              ctr = this.view.el.select('.fc-event-container',true).first();
59612         }
59613         
59614          
59615         this.clearEvent(ev);
59616             //code
59617        
59618         
59619         
59620         ev.els = [];
59621         var cells = ev.cells;
59622         var rows = ev.rows;
59623         this.fireEvent('eventrender', this, ev);
59624         
59625         for(var i =0; i < rows.length; i++) {
59626             
59627             cls = '';
59628             if (i == 0) {
59629                 cls += ' fc-event-start';
59630             }
59631             if ((i+1) == rows.length) {
59632                 cls += ' fc-event-end';
59633             }
59634             
59635             //Roo.log(ev.data);
59636             // how many rows should it span..
59637             var cg = this.eventTmpl.append(ctr,Roo.apply({
59638                 fccls : cls
59639                 
59640             }, ev.data) , true);
59641             
59642             
59643             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59644             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59645             cg.on('click', this.onEventClick, this, ev);
59646             
59647             ev.els.push(cg);
59648             
59649             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59650             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59651             //Roo.log(cg);
59652              
59653             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59654             cg.setWidth(ebox.right - sbox.x -2);
59655         }
59656     },
59657     
59658     renderEvents: function()
59659     {   
59660         // first make sure there is enough space..
59661         
59662         if (!this.eventTmpl) {
59663             this.eventTmpl = new Roo.Template(
59664                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59665                     '<div class="fc-event-inner">' +
59666                         '<span class="fc-event-time">{time}</span>' +
59667                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59668                     '</div>' +
59669                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59670                 '</div>'
59671             );
59672                 
59673         }
59674                
59675         
59676         
59677         this.cells.each(function(c) {
59678             //Roo.log(c.select('.fc-day-content div',true).first());
59679             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59680         });
59681         
59682         var ctr = this.view.el.select('.fc-event-container',true).first();
59683         
59684         var cls;
59685         this.eventStore.each(function(ev){
59686             
59687             this.renderEvent(ev);
59688              
59689              
59690         }, this);
59691         this.view.layout();
59692         
59693     },
59694     
59695     onEventEnter: function (e, el,event,d) {
59696         this.fireEvent('evententer', this, el, event);
59697     },
59698     
59699     onEventLeave: function (e, el,event,d) {
59700         this.fireEvent('eventleave', this, el, event);
59701     },
59702     
59703     onEventClick: function (e, el,event,d) {
59704         this.fireEvent('eventclick', this, el, event);
59705     },
59706     
59707     onMonthChange: function () {
59708         this.store.load();
59709     },
59710     
59711     onLoad: function () {
59712         
59713         //Roo.log('calendar onload');
59714 //         
59715         if(this.eventStore.getCount() > 0){
59716             
59717            
59718             
59719             this.eventStore.each(function(d){
59720                 
59721                 
59722                 // FIXME..
59723                 var add =   d.data;
59724                 if (typeof(add.end_dt) == 'undefined')  {
59725                     Roo.log("Missing End time in calendar data: ");
59726                     Roo.log(d);
59727                     return;
59728                 }
59729                 if (typeof(add.start_dt) == 'undefined')  {
59730                     Roo.log("Missing Start time in calendar data: ");
59731                     Roo.log(d);
59732                     return;
59733                 }
59734                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59735                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59736                 add.id = add.id || d.id;
59737                 add.title = add.title || '??';
59738                 
59739                 this.addItem(d);
59740                 
59741              
59742             },this);
59743         }
59744         
59745         this.renderEvents();
59746     }
59747     
59748
59749 });
59750 /*
59751  grid : {
59752                 xtype: 'Grid',
59753                 xns: Roo.grid,
59754                 listeners : {
59755                     render : function ()
59756                     {
59757                         _this.grid = this;
59758                         
59759                         if (!this.view.el.hasClass('course-timesheet')) {
59760                             this.view.el.addClass('course-timesheet');
59761                         }
59762                         if (this.tsStyle) {
59763                             this.ds.load({});
59764                             return; 
59765                         }
59766                         Roo.log('width');
59767                         Roo.log(_this.grid.view.el.getWidth());
59768                         
59769                         
59770                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59771                             '.course-timesheet .x-grid-row' : {
59772                                 height: '80px'
59773                             },
59774                             '.x-grid-row td' : {
59775                                 'vertical-align' : 0
59776                             },
59777                             '.course-edit-link' : {
59778                                 'color' : 'blue',
59779                                 'text-overflow' : 'ellipsis',
59780                                 'overflow' : 'hidden',
59781                                 'white-space' : 'nowrap',
59782                                 'cursor' : 'pointer'
59783                             },
59784                             '.sub-link' : {
59785                                 'color' : 'green'
59786                             },
59787                             '.de-act-sup-link' : {
59788                                 'color' : 'purple',
59789                                 'text-decoration' : 'line-through'
59790                             },
59791                             '.de-act-link' : {
59792                                 'color' : 'red',
59793                                 'text-decoration' : 'line-through'
59794                             },
59795                             '.course-timesheet .course-highlight' : {
59796                                 'border-top-style': 'dashed !important',
59797                                 'border-bottom-bottom': 'dashed !important'
59798                             },
59799                             '.course-timesheet .course-item' : {
59800                                 'font-family'   : 'tahoma, arial, helvetica',
59801                                 'font-size'     : '11px',
59802                                 'overflow'      : 'hidden',
59803                                 'padding-left'  : '10px',
59804                                 'padding-right' : '10px',
59805                                 'padding-top' : '10px' 
59806                             }
59807                             
59808                         }, Roo.id());
59809                                 this.ds.load({});
59810                     }
59811                 },
59812                 autoWidth : true,
59813                 monitorWindowResize : false,
59814                 cellrenderer : function(v,x,r)
59815                 {
59816                     return v;
59817                 },
59818                 sm : {
59819                     xtype: 'CellSelectionModel',
59820                     xns: Roo.grid
59821                 },
59822                 dataSource : {
59823                     xtype: 'Store',
59824                     xns: Roo.data,
59825                     listeners : {
59826                         beforeload : function (_self, options)
59827                         {
59828                             options.params = options.params || {};
59829                             options.params._month = _this.monthField.getValue();
59830                             options.params.limit = 9999;
59831                             options.params['sort'] = 'when_dt';    
59832                             options.params['dir'] = 'ASC';    
59833                             this.proxy.loadResponse = this.loadResponse;
59834                             Roo.log("load?");
59835                             //this.addColumns();
59836                         },
59837                         load : function (_self, records, options)
59838                         {
59839                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59840                                 // if you click on the translation.. you can edit it...
59841                                 var el = Roo.get(this);
59842                                 var id = el.dom.getAttribute('data-id');
59843                                 var d = el.dom.getAttribute('data-date');
59844                                 var t = el.dom.getAttribute('data-time');
59845                                 //var id = this.child('span').dom.textContent;
59846                                 
59847                                 //Roo.log(this);
59848                                 Pman.Dialog.CourseCalendar.show({
59849                                     id : id,
59850                                     when_d : d,
59851                                     when_t : t,
59852                                     productitem_active : id ? 1 : 0
59853                                 }, function() {
59854                                     _this.grid.ds.load({});
59855                                 });
59856                            
59857                            });
59858                            
59859                            _this.panel.fireEvent('resize', [ '', '' ]);
59860                         }
59861                     },
59862                     loadResponse : function(o, success, response){
59863                             // this is overridden on before load..
59864                             
59865                             Roo.log("our code?");       
59866                             //Roo.log(success);
59867                             //Roo.log(response)
59868                             delete this.activeRequest;
59869                             if(!success){
59870                                 this.fireEvent("loadexception", this, o, response);
59871                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59872                                 return;
59873                             }
59874                             var result;
59875                             try {
59876                                 result = o.reader.read(response);
59877                             }catch(e){
59878                                 Roo.log("load exception?");
59879                                 this.fireEvent("loadexception", this, o, response, e);
59880                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59881                                 return;
59882                             }
59883                             Roo.log("ready...");        
59884                             // loop through result.records;
59885                             // and set this.tdate[date] = [] << array of records..
59886                             _this.tdata  = {};
59887                             Roo.each(result.records, function(r){
59888                                 //Roo.log(r.data);
59889                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59890                                     _this.tdata[r.data.when_dt.format('j')] = [];
59891                                 }
59892                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59893                             });
59894                             
59895                             //Roo.log(_this.tdata);
59896                             
59897                             result.records = [];
59898                             result.totalRecords = 6;
59899                     
59900                             // let's generate some duumy records for the rows.
59901                             //var st = _this.dateField.getValue();
59902                             
59903                             // work out monday..
59904                             //st = st.add(Date.DAY, -1 * st.format('w'));
59905                             
59906                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59907                             
59908                             var firstOfMonth = date.getFirstDayOfMonth();
59909                             var days = date.getDaysInMonth();
59910                             var d = 1;
59911                             var firstAdded = false;
59912                             for (var i = 0; i < result.totalRecords ; i++) {
59913                                 //var d= st.add(Date.DAY, i);
59914                                 var row = {};
59915                                 var added = 0;
59916                                 for(var w = 0 ; w < 7 ; w++){
59917                                     if(!firstAdded && firstOfMonth != w){
59918                                         continue;
59919                                     }
59920                                     if(d > days){
59921                                         continue;
59922                                     }
59923                                     firstAdded = true;
59924                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59925                                     row['weekday'+w] = String.format(
59926                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59927                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59928                                                     d,
59929                                                     date.format('Y-m-')+dd
59930                                                 );
59931                                     added++;
59932                                     if(typeof(_this.tdata[d]) != 'undefined'){
59933                                         Roo.each(_this.tdata[d], function(r){
59934                                             var is_sub = '';
59935                                             var deactive = '';
59936                                             var id = r.id;
59937                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59938                                             if(r.parent_id*1>0){
59939                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59940                                                 id = r.parent_id;
59941                                             }
59942                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59943                                                 deactive = 'de-act-link';
59944                                             }
59945                                             
59946                                             row['weekday'+w] += String.format(
59947                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59948                                                     id, //0
59949                                                     r.product_id_name, //1
59950                                                     r.when_dt.format('h:ia'), //2
59951                                                     is_sub, //3
59952                                                     deactive, //4
59953                                                     desc // 5
59954                                             );
59955                                         });
59956                                     }
59957                                     d++;
59958                                 }
59959                                 
59960                                 // only do this if something added..
59961                                 if(added > 0){ 
59962                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59963                                 }
59964                                 
59965                                 
59966                                 // push it twice. (second one with an hour..
59967                                 
59968                             }
59969                             //Roo.log(result);
59970                             this.fireEvent("load", this, o, o.request.arg);
59971                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59972                         },
59973                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59974                     proxy : {
59975                         xtype: 'HttpProxy',
59976                         xns: Roo.data,
59977                         method : 'GET',
59978                         url : baseURL + '/Roo/Shop_course.php'
59979                     },
59980                     reader : {
59981                         xtype: 'JsonReader',
59982                         xns: Roo.data,
59983                         id : 'id',
59984                         fields : [
59985                             {
59986                                 'name': 'id',
59987                                 'type': 'int'
59988                             },
59989                             {
59990                                 'name': 'when_dt',
59991                                 'type': 'string'
59992                             },
59993                             {
59994                                 'name': 'end_dt',
59995                                 'type': 'string'
59996                             },
59997                             {
59998                                 'name': 'parent_id',
59999                                 'type': 'int'
60000                             },
60001                             {
60002                                 'name': 'product_id',
60003                                 'type': 'int'
60004                             },
60005                             {
60006                                 'name': 'productitem_id',
60007                                 'type': 'int'
60008                             },
60009                             {
60010                                 'name': 'guid',
60011                                 'type': 'int'
60012                             }
60013                         ]
60014                     }
60015                 },
60016                 toolbar : {
60017                     xtype: 'Toolbar',
60018                     xns: Roo,
60019                     items : [
60020                         {
60021                             xtype: 'Button',
60022                             xns: Roo.Toolbar,
60023                             listeners : {
60024                                 click : function (_self, e)
60025                                 {
60026                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60027                                     sd.setMonth(sd.getMonth()-1);
60028                                     _this.monthField.setValue(sd.format('Y-m-d'));
60029                                     _this.grid.ds.load({});
60030                                 }
60031                             },
60032                             text : "Back"
60033                         },
60034                         {
60035                             xtype: 'Separator',
60036                             xns: Roo.Toolbar
60037                         },
60038                         {
60039                             xtype: 'MonthField',
60040                             xns: Roo.form,
60041                             listeners : {
60042                                 render : function (_self)
60043                                 {
60044                                     _this.monthField = _self;
60045                                    // _this.monthField.set  today
60046                                 },
60047                                 select : function (combo, date)
60048                                 {
60049                                     _this.grid.ds.load({});
60050                                 }
60051                             },
60052                             value : (function() { return new Date(); })()
60053                         },
60054                         {
60055                             xtype: 'Separator',
60056                             xns: Roo.Toolbar
60057                         },
60058                         {
60059                             xtype: 'TextItem',
60060                             xns: Roo.Toolbar,
60061                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60062                         },
60063                         {
60064                             xtype: 'Fill',
60065                             xns: Roo.Toolbar
60066                         },
60067                         {
60068                             xtype: 'Button',
60069                             xns: Roo.Toolbar,
60070                             listeners : {
60071                                 click : function (_self, e)
60072                                 {
60073                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60074                                     sd.setMonth(sd.getMonth()+1);
60075                                     _this.monthField.setValue(sd.format('Y-m-d'));
60076                                     _this.grid.ds.load({});
60077                                 }
60078                             },
60079                             text : "Next"
60080                         }
60081                     ]
60082                 },
60083                  
60084             }
60085         };
60086         
60087         *//*
60088  * Based on:
60089  * Ext JS Library 1.1.1
60090  * Copyright(c) 2006-2007, Ext JS, LLC.
60091  *
60092  * Originally Released Under LGPL - original licence link has changed is not relivant.
60093  *
60094  * Fork - LGPL
60095  * <script type="text/javascript">
60096  */
60097  
60098 /**
60099  * @class Roo.LoadMask
60100  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60101  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60102  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60103  * element's UpdateManager load indicator and will be destroyed after the initial load.
60104  * @constructor
60105  * Create a new LoadMask
60106  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60107  * @param {Object} config The config object
60108  */
60109 Roo.LoadMask = function(el, config){
60110     this.el = Roo.get(el);
60111     Roo.apply(this, config);
60112     if(this.store){
60113         this.store.on('beforeload', this.onBeforeLoad, this);
60114         this.store.on('load', this.onLoad, this);
60115         this.store.on('loadexception', this.onLoadException, this);
60116         this.removeMask = false;
60117     }else{
60118         var um = this.el.getUpdateManager();
60119         um.showLoadIndicator = false; // disable the default indicator
60120         um.on('beforeupdate', this.onBeforeLoad, this);
60121         um.on('update', this.onLoad, this);
60122         um.on('failure', this.onLoad, this);
60123         this.removeMask = true;
60124     }
60125 };
60126
60127 Roo.LoadMask.prototype = {
60128     /**
60129      * @cfg {Boolean} removeMask
60130      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60131      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60132      */
60133     /**
60134      * @cfg {String} msg
60135      * The text to display in a centered loading message box (defaults to 'Loading...')
60136      */
60137     msg : 'Loading...',
60138     /**
60139      * @cfg {String} msgCls
60140      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60141      */
60142     msgCls : 'x-mask-loading',
60143
60144     /**
60145      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60146      * @type Boolean
60147      */
60148     disabled: false,
60149
60150     /**
60151      * Disables the mask to prevent it from being displayed
60152      */
60153     disable : function(){
60154        this.disabled = true;
60155     },
60156
60157     /**
60158      * Enables the mask so that it can be displayed
60159      */
60160     enable : function(){
60161         this.disabled = false;
60162     },
60163     
60164     onLoadException : function()
60165     {
60166         Roo.log(arguments);
60167         
60168         if (typeof(arguments[3]) != 'undefined') {
60169             Roo.MessageBox.alert("Error loading",arguments[3]);
60170         } 
60171         /*
60172         try {
60173             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60174                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60175             }   
60176         } catch(e) {
60177             
60178         }
60179         */
60180     
60181         
60182         
60183         this.el.unmask(this.removeMask);
60184     },
60185     // private
60186     onLoad : function()
60187     {
60188         this.el.unmask(this.removeMask);
60189     },
60190
60191     // private
60192     onBeforeLoad : function(){
60193         if(!this.disabled){
60194             this.el.mask(this.msg, this.msgCls);
60195         }
60196     },
60197
60198     // private
60199     destroy : function(){
60200         if(this.store){
60201             this.store.un('beforeload', this.onBeforeLoad, this);
60202             this.store.un('load', this.onLoad, this);
60203             this.store.un('loadexception', this.onLoadException, this);
60204         }else{
60205             var um = this.el.getUpdateManager();
60206             um.un('beforeupdate', this.onBeforeLoad, this);
60207             um.un('update', this.onLoad, this);
60208             um.un('failure', this.onLoad, this);
60209         }
60210     }
60211 };/*
60212  * Based on:
60213  * Ext JS Library 1.1.1
60214  * Copyright(c) 2006-2007, Ext JS, LLC.
60215  *
60216  * Originally Released Under LGPL - original licence link has changed is not relivant.
60217  *
60218  * Fork - LGPL
60219  * <script type="text/javascript">
60220  */
60221
60222
60223 /**
60224  * @class Roo.XTemplate
60225  * @extends Roo.Template
60226  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60227 <pre><code>
60228 var t = new Roo.XTemplate(
60229         '&lt;select name="{name}"&gt;',
60230                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60231         '&lt;/select&gt;'
60232 );
60233  
60234 // then append, applying the master template values
60235  </code></pre>
60236  *
60237  * Supported features:
60238  *
60239  *  Tags:
60240
60241 <pre><code>
60242       {a_variable} - output encoded.
60243       {a_variable.format:("Y-m-d")} - call a method on the variable
60244       {a_variable:raw} - unencoded output
60245       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60246       {a_variable:this.method_on_template(...)} - call a method on the template object.
60247  
60248 </code></pre>
60249  *  The tpl tag:
60250 <pre><code>
60251         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60252         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60253         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60254         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60255   
60256         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60257         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60258 </code></pre>
60259  *      
60260  */
60261 Roo.XTemplate = function()
60262 {
60263     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60264     if (this.html) {
60265         this.compile();
60266     }
60267 };
60268
60269
60270 Roo.extend(Roo.XTemplate, Roo.Template, {
60271
60272     /**
60273      * The various sub templates
60274      */
60275     tpls : false,
60276     /**
60277      *
60278      * basic tag replacing syntax
60279      * WORD:WORD()
60280      *
60281      * // you can fake an object call by doing this
60282      *  x.t:(test,tesT) 
60283      * 
60284      */
60285     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60286
60287     /**
60288      * compile the template
60289      *
60290      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60291      *
60292      */
60293     compile: function()
60294     {
60295         var s = this.html;
60296      
60297         s = ['<tpl>', s, '</tpl>'].join('');
60298     
60299         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60300             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60301             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60302             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60303             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60304             m,
60305             id     = 0,
60306             tpls   = [];
60307     
60308         while(true == !!(m = s.match(re))){
60309             var forMatch   = m[0].match(nameRe),
60310                 ifMatch   = m[0].match(ifRe),
60311                 execMatch   = m[0].match(execRe),
60312                 namedMatch   = m[0].match(namedRe),
60313                 
60314                 exp  = null, 
60315                 fn   = null,
60316                 exec = null,
60317                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60318                 
60319             if (ifMatch) {
60320                 // if - puts fn into test..
60321                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60322                 if(exp){
60323                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60324                 }
60325             }
60326             
60327             if (execMatch) {
60328                 // exec - calls a function... returns empty if true is  returned.
60329                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60330                 if(exp){
60331                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60332                 }
60333             }
60334             
60335             
60336             if (name) {
60337                 // for = 
60338                 switch(name){
60339                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60340                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60341                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60342                 }
60343             }
60344             var uid = namedMatch ? namedMatch[1] : id;
60345             
60346             
60347             tpls.push({
60348                 id:     namedMatch ? namedMatch[1] : id,
60349                 target: name,
60350                 exec:   exec,
60351                 test:   fn,
60352                 body:   m[1] || ''
60353             });
60354             if (namedMatch) {
60355                 s = s.replace(m[0], '');
60356             } else { 
60357                 s = s.replace(m[0], '{xtpl'+ id + '}');
60358             }
60359             ++id;
60360         }
60361         this.tpls = [];
60362         for(var i = tpls.length-1; i >= 0; --i){
60363             this.compileTpl(tpls[i]);
60364             this.tpls[tpls[i].id] = tpls[i];
60365         }
60366         this.master = tpls[tpls.length-1];
60367         return this;
60368     },
60369     /**
60370      * same as applyTemplate, except it's done to one of the subTemplates
60371      * when using named templates, you can do:
60372      *
60373      * var str = pl.applySubTemplate('your-name', values);
60374      *
60375      * 
60376      * @param {Number} id of the template
60377      * @param {Object} values to apply to template
60378      * @param {Object} parent (normaly the instance of this object)
60379      */
60380     applySubTemplate : function(id, values, parent)
60381     {
60382         
60383         
60384         var t = this.tpls[id];
60385         
60386         
60387         try { 
60388             if(t.test && !t.test.call(this, values, parent)){
60389                 return '';
60390             }
60391         } catch(e) {
60392             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60393             Roo.log(e.toString());
60394             Roo.log(t.test);
60395             return ''
60396         }
60397         try { 
60398             
60399             if(t.exec && t.exec.call(this, values, parent)){
60400                 return '';
60401             }
60402         } catch(e) {
60403             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60404             Roo.log(e.toString());
60405             Roo.log(t.exec);
60406             return ''
60407         }
60408         try {
60409             var vs = t.target ? t.target.call(this, values, parent) : values;
60410             parent = t.target ? values : parent;
60411             if(t.target && vs instanceof Array){
60412                 var buf = [];
60413                 for(var i = 0, len = vs.length; i < len; i++){
60414                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60415                 }
60416                 return buf.join('');
60417             }
60418             return t.compiled.call(this, vs, parent);
60419         } catch (e) {
60420             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60421             Roo.log(e.toString());
60422             Roo.log(t.compiled);
60423             return '';
60424         }
60425     },
60426
60427     compileTpl : function(tpl)
60428     {
60429         var fm = Roo.util.Format;
60430         var useF = this.disableFormats !== true;
60431         var sep = Roo.isGecko ? "+" : ",";
60432         var undef = function(str) {
60433             Roo.log("Property not found :"  + str);
60434             return '';
60435         };
60436         
60437         var fn = function(m, name, format, args)
60438         {
60439             //Roo.log(arguments);
60440             args = args ? args.replace(/\\'/g,"'") : args;
60441             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60442             if (typeof(format) == 'undefined') {
60443                 format= 'htmlEncode';
60444             }
60445             if (format == 'raw' ) {
60446                 format = false;
60447             }
60448             
60449             if(name.substr(0, 4) == 'xtpl'){
60450                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60451             }
60452             
60453             // build an array of options to determine if value is undefined..
60454             
60455             // basically get 'xxxx.yyyy' then do
60456             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60457             //    (function () { Roo.log("Property not found"); return ''; })() :
60458             //    ......
60459             
60460             var udef_ar = [];
60461             var lookfor = '';
60462             Roo.each(name.split('.'), function(st) {
60463                 lookfor += (lookfor.length ? '.': '') + st;
60464                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60465             });
60466             
60467             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60468             
60469             
60470             if(format && useF){
60471                 
60472                 args = args ? ',' + args : "";
60473                  
60474                 if(format.substr(0, 5) != "this."){
60475                     format = "fm." + format + '(';
60476                 }else{
60477                     format = 'this.call("'+ format.substr(5) + '", ';
60478                     args = ", values";
60479                 }
60480                 
60481                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60482             }
60483              
60484             if (args.length) {
60485                 // called with xxyx.yuu:(test,test)
60486                 // change to ()
60487                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60488             }
60489             // raw.. - :raw modifier..
60490             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60491             
60492         };
60493         var body;
60494         // branched to use + in gecko and [].join() in others
60495         if(Roo.isGecko){
60496             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60497                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60498                     "';};};";
60499         }else{
60500             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60501             body.push(tpl.body.replace(/(\r\n|\n)/g,
60502                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60503             body.push("'].join('');};};");
60504             body = body.join('');
60505         }
60506         
60507         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60508        
60509         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60510         eval(body);
60511         
60512         return this;
60513     },
60514
60515     applyTemplate : function(values){
60516         return this.master.compiled.call(this, values, {});
60517         //var s = this.subs;
60518     },
60519
60520     apply : function(){
60521         return this.applyTemplate.apply(this, arguments);
60522     }
60523
60524  });
60525
60526 Roo.XTemplate.from = function(el){
60527     el = Roo.getDom(el);
60528     return new Roo.XTemplate(el.value || el.innerHTML);
60529 };