roojs-ui.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  * Based on:
6059  * Ext JS Library 1.1.1
6060  * Copyright(c) 2006-2007, Ext JS, LLC.
6061  *
6062  * Originally Released Under LGPL - original licence link has changed is not relivant.
6063  *
6064  * Fork - LGPL
6065  * <script type="text/javascript">
6066  */
6067
6068 /**
6069  * @class Roo.EventManager
6070  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6071  * several useful events directly.
6072  * See {@link Roo.EventObject} for more details on normalized event objects.
6073  * @singleton
6074  */
6075 Roo.EventManager = function(){
6076     var docReadyEvent, docReadyProcId, docReadyState = false;
6077     var resizeEvent, resizeTask, textEvent, textSize;
6078     var E = Roo.lib.Event;
6079     var D = Roo.lib.Dom;
6080
6081     
6082     
6083
6084     var fireDocReady = function(){
6085         if(!docReadyState){
6086             docReadyState = true;
6087             Roo.isReady = true;
6088             if(docReadyProcId){
6089                 clearInterval(docReadyProcId);
6090             }
6091             if(Roo.isGecko || Roo.isOpera) {
6092                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6093             }
6094             if(Roo.isIE){
6095                 var defer = document.getElementById("ie-deferred-loader");
6096                 if(defer){
6097                     defer.onreadystatechange = null;
6098                     defer.parentNode.removeChild(defer);
6099                 }
6100             }
6101             if(docReadyEvent){
6102                 docReadyEvent.fire();
6103                 docReadyEvent.clearListeners();
6104             }
6105         }
6106     };
6107     
6108     var initDocReady = function(){
6109         docReadyEvent = new Roo.util.Event();
6110         if(Roo.isGecko || Roo.isOpera) {
6111             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6112         }else if(Roo.isIE){
6113             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6114             var defer = document.getElementById("ie-deferred-loader");
6115             defer.onreadystatechange = function(){
6116                 if(this.readyState == "complete"){
6117                     fireDocReady();
6118                 }
6119             };
6120         }else if(Roo.isSafari){ 
6121             docReadyProcId = setInterval(function(){
6122                 var rs = document.readyState;
6123                 if(rs == "complete") {
6124                     fireDocReady();     
6125                  }
6126             }, 10);
6127         }
6128         // no matter what, make sure it fires on load
6129         E.on(window, "load", fireDocReady);
6130     };
6131
6132     var createBuffered = function(h, o){
6133         var task = new Roo.util.DelayedTask(h);
6134         return function(e){
6135             // create new event object impl so new events don't wipe out properties
6136             e = new Roo.EventObjectImpl(e);
6137             task.delay(o.buffer, h, null, [e]);
6138         };
6139     };
6140
6141     var createSingle = function(h, el, ename, fn){
6142         return function(e){
6143             Roo.EventManager.removeListener(el, ename, fn);
6144             h(e);
6145         };
6146     };
6147
6148     var createDelayed = function(h, o){
6149         return function(e){
6150             // create new event object impl so new events don't wipe out properties
6151             e = new Roo.EventObjectImpl(e);
6152             setTimeout(function(){
6153                 h(e);
6154             }, o.delay || 10);
6155         };
6156     };
6157     var transitionEndVal = false;
6158     
6159     var transitionEnd = function()
6160     {
6161         if (transitionEndVal) {
6162             return transitionEndVal;
6163         }
6164         var el = document.createElement('div');
6165
6166         var transEndEventNames = {
6167             WebkitTransition : 'webkitTransitionEnd',
6168             MozTransition    : 'transitionend',
6169             OTransition      : 'oTransitionEnd otransitionend',
6170             transition       : 'transitionend'
6171         };
6172     
6173         for (var name in transEndEventNames) {
6174             if (el.style[name] !== undefined) {
6175                 transitionEndVal = transEndEventNames[name];
6176                 return  transitionEndVal ;
6177             }
6178         }
6179     }
6180     
6181
6182     var listen = function(element, ename, opt, fn, scope){
6183         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6184         fn = fn || o.fn; scope = scope || o.scope;
6185         var el = Roo.getDom(element);
6186         
6187         
6188         if(!el){
6189             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6190         }
6191         
6192         if (ename == 'transitionend') {
6193             ename = transitionEnd();
6194         }
6195         var h = function(e){
6196             e = Roo.EventObject.setEvent(e);
6197             var t;
6198             if(o.delegate){
6199                 t = e.getTarget(o.delegate, el);
6200                 if(!t){
6201                     return;
6202                 }
6203             }else{
6204                 t = e.target;
6205             }
6206             if(o.stopEvent === true){
6207                 e.stopEvent();
6208             }
6209             if(o.preventDefault === true){
6210                e.preventDefault();
6211             }
6212             if(o.stopPropagation === true){
6213                 e.stopPropagation();
6214             }
6215
6216             if(o.normalized === false){
6217                 e = e.browserEvent;
6218             }
6219
6220             fn.call(scope || el, e, t, o);
6221         };
6222         if(o.delay){
6223             h = createDelayed(h, o);
6224         }
6225         if(o.single){
6226             h = createSingle(h, el, ename, fn);
6227         }
6228         if(o.buffer){
6229             h = createBuffered(h, o);
6230         }
6231         fn._handlers = fn._handlers || [];
6232         
6233         
6234         fn._handlers.push([Roo.id(el), ename, h]);
6235         
6236         
6237          
6238         E.on(el, ename, h);
6239         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6240             el.addEventListener("DOMMouseScroll", h, false);
6241             E.on(window, 'unload', function(){
6242                 el.removeEventListener("DOMMouseScroll", h, false);
6243             });
6244         }
6245         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6246             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6247         }
6248         return h;
6249     };
6250
6251     var stopListening = function(el, ename, fn){
6252         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6253         if(hds){
6254             for(var i = 0, len = hds.length; i < len; i++){
6255                 var h = hds[i];
6256                 if(h[0] == id && h[1] == ename){
6257                     hd = h[2];
6258                     hds.splice(i, 1);
6259                     break;
6260                 }
6261             }
6262         }
6263         E.un(el, ename, hd);
6264         el = Roo.getDom(el);
6265         if(ename == "mousewheel" && el.addEventListener){
6266             el.removeEventListener("DOMMouseScroll", hd, false);
6267         }
6268         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6269             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6270         }
6271     };
6272
6273     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6274     
6275     var pub = {
6276         
6277         
6278         /** 
6279          * Fix for doc tools
6280          * @scope Roo.EventManager
6281          */
6282         
6283         
6284         /** 
6285          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6286          * object with a Roo.EventObject
6287          * @param {Function} fn        The method the event invokes
6288          * @param {Object}   scope    An object that becomes the scope of the handler
6289          * @param {boolean}  override If true, the obj passed in becomes
6290          *                             the execution scope of the listener
6291          * @return {Function} The wrapped function
6292          * @deprecated
6293          */
6294         wrap : function(fn, scope, override){
6295             return function(e){
6296                 Roo.EventObject.setEvent(e);
6297                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6298             };
6299         },
6300         
6301         /**
6302      * Appends an event handler to an element (shorthand for addListener)
6303      * @param {String/HTMLElement}   element        The html element or id to assign the
6304      * @param {String}   eventName The type of event to listen for
6305      * @param {Function} handler The method the event invokes
6306      * @param {Object}   scope (optional) The scope in which to execute the handler
6307      * function. The handler function's "this" context.
6308      * @param {Object}   options (optional) An object containing handler configuration
6309      * properties. This may contain any of the following properties:<ul>
6310      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6311      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6312      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6313      * <li>preventDefault {Boolean} True to prevent the default action</li>
6314      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6315      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6316      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6317      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6318      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6319      * by the specified number of milliseconds. If the event fires again within that time, the original
6320      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6321      * </ul><br>
6322      * <p>
6323      * <b>Combining Options</b><br>
6324      * Using the options argument, it is possible to combine different types of listeners:<br>
6325      * <br>
6326      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6327      * Code:<pre><code>
6328 el.on('click', this.onClick, this, {
6329     single: true,
6330     delay: 100,
6331     stopEvent : true,
6332     forumId: 4
6333 });</code></pre>
6334      * <p>
6335      * <b>Attaching multiple handlers in 1 call</b><br>
6336       * The method also allows for a single argument to be passed which is a config object containing properties
6337      * which specify multiple handlers.
6338      * <p>
6339      * Code:<pre><code>
6340 el.on({
6341     'click' : {
6342         fn: this.onClick
6343         scope: this,
6344         delay: 100
6345     },
6346     'mouseover' : {
6347         fn: this.onMouseOver
6348         scope: this
6349     },
6350     'mouseout' : {
6351         fn: this.onMouseOut
6352         scope: this
6353     }
6354 });</code></pre>
6355      * <p>
6356      * Or a shorthand syntax:<br>
6357      * Code:<pre><code>
6358 el.on({
6359     'click' : this.onClick,
6360     'mouseover' : this.onMouseOver,
6361     'mouseout' : this.onMouseOut
6362     scope: this
6363 });</code></pre>
6364      */
6365         addListener : function(element, eventName, fn, scope, options){
6366             if(typeof eventName == "object"){
6367                 var o = eventName;
6368                 for(var e in o){
6369                     if(propRe.test(e)){
6370                         continue;
6371                     }
6372                     if(typeof o[e] == "function"){
6373                         // shared options
6374                         listen(element, e, o, o[e], o.scope);
6375                     }else{
6376                         // individual options
6377                         listen(element, e, o[e]);
6378                     }
6379                 }
6380                 return;
6381             }
6382             return listen(element, eventName, options, fn, scope);
6383         },
6384         
6385         /**
6386          * Removes an event handler
6387          *
6388          * @param {String/HTMLElement}   element        The id or html element to remove the 
6389          *                             event from
6390          * @param {String}   eventName     The type of event
6391          * @param {Function} fn
6392          * @return {Boolean} True if a listener was actually removed
6393          */
6394         removeListener : function(element, eventName, fn){
6395             return stopListening(element, eventName, fn);
6396         },
6397         
6398         /**
6399          * Fires when the document is ready (before onload and before images are loaded). Can be 
6400          * accessed shorthanded Roo.onReady().
6401          * @param {Function} fn        The method the event invokes
6402          * @param {Object}   scope    An  object that becomes the scope of the handler
6403          * @param {boolean}  options
6404          */
6405         onDocumentReady : function(fn, scope, options){
6406             if(docReadyState){ // if it already fired
6407                 docReadyEvent.addListener(fn, scope, options);
6408                 docReadyEvent.fire();
6409                 docReadyEvent.clearListeners();
6410                 return;
6411             }
6412             if(!docReadyEvent){
6413                 initDocReady();
6414             }
6415             docReadyEvent.addListener(fn, scope, options);
6416         },
6417         
6418         /**
6419          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6420          * @param {Function} fn        The method the event invokes
6421          * @param {Object}   scope    An object that becomes the scope of the handler
6422          * @param {boolean}  options
6423          */
6424         onWindowResize : function(fn, scope, options){
6425             if(!resizeEvent){
6426                 resizeEvent = new Roo.util.Event();
6427                 resizeTask = new Roo.util.DelayedTask(function(){
6428                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6429                 });
6430                 E.on(window, "resize", function(){
6431                     if(Roo.isIE){
6432                         resizeTask.delay(50);
6433                     }else{
6434                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6435                     }
6436                 });
6437             }
6438             resizeEvent.addListener(fn, scope, options);
6439         },
6440
6441         /**
6442          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6443          * @param {Function} fn        The method the event invokes
6444          * @param {Object}   scope    An object that becomes the scope of the handler
6445          * @param {boolean}  options
6446          */
6447         onTextResize : function(fn, scope, options){
6448             if(!textEvent){
6449                 textEvent = new Roo.util.Event();
6450                 var textEl = new Roo.Element(document.createElement('div'));
6451                 textEl.dom.className = 'x-text-resize';
6452                 textEl.dom.innerHTML = 'X';
6453                 textEl.appendTo(document.body);
6454                 textSize = textEl.dom.offsetHeight;
6455                 setInterval(function(){
6456                     if(textEl.dom.offsetHeight != textSize){
6457                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6458                     }
6459                 }, this.textResizeInterval);
6460             }
6461             textEvent.addListener(fn, scope, options);
6462         },
6463
6464         /**
6465          * Removes the passed window resize listener.
6466          * @param {Function} fn        The method the event invokes
6467          * @param {Object}   scope    The scope of handler
6468          */
6469         removeResizeListener : function(fn, scope){
6470             if(resizeEvent){
6471                 resizeEvent.removeListener(fn, scope);
6472             }
6473         },
6474
6475         // private
6476         fireResize : function(){
6477             if(resizeEvent){
6478                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6479             }   
6480         },
6481         /**
6482          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6483          */
6484         ieDeferSrc : false,
6485         /**
6486          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6487          */
6488         textResizeInterval : 50
6489     };
6490     
6491     /**
6492      * Fix for doc tools
6493      * @scopeAlias pub=Roo.EventManager
6494      */
6495     
6496      /**
6497      * Appends an event handler to an element (shorthand for addListener)
6498      * @param {String/HTMLElement}   element        The html element or id to assign the
6499      * @param {String}   eventName The type of event to listen for
6500      * @param {Function} handler The method the event invokes
6501      * @param {Object}   scope (optional) The scope in which to execute the handler
6502      * function. The handler function's "this" context.
6503      * @param {Object}   options (optional) An object containing handler configuration
6504      * properties. This may contain any of the following properties:<ul>
6505      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6506      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6507      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6508      * <li>preventDefault {Boolean} True to prevent the default action</li>
6509      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6510      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6511      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6512      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6513      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6514      * by the specified number of milliseconds. If the event fires again within that time, the original
6515      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6516      * </ul><br>
6517      * <p>
6518      * <b>Combining Options</b><br>
6519      * Using the options argument, it is possible to combine different types of listeners:<br>
6520      * <br>
6521      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6522      * Code:<pre><code>
6523 el.on('click', this.onClick, this, {
6524     single: true,
6525     delay: 100,
6526     stopEvent : true,
6527     forumId: 4
6528 });</code></pre>
6529      * <p>
6530      * <b>Attaching multiple handlers in 1 call</b><br>
6531       * The method also allows for a single argument to be passed which is a config object containing properties
6532      * which specify multiple handlers.
6533      * <p>
6534      * Code:<pre><code>
6535 el.on({
6536     'click' : {
6537         fn: this.onClick
6538         scope: this,
6539         delay: 100
6540     },
6541     'mouseover' : {
6542         fn: this.onMouseOver
6543         scope: this
6544     },
6545     'mouseout' : {
6546         fn: this.onMouseOut
6547         scope: this
6548     }
6549 });</code></pre>
6550      * <p>
6551      * Or a shorthand syntax:<br>
6552      * Code:<pre><code>
6553 el.on({
6554     'click' : this.onClick,
6555     'mouseover' : this.onMouseOver,
6556     'mouseout' : this.onMouseOut
6557     scope: this
6558 });</code></pre>
6559      */
6560     pub.on = pub.addListener;
6561     pub.un = pub.removeListener;
6562
6563     pub.stoppedMouseDownEvent = new Roo.util.Event();
6564     return pub;
6565 }();
6566 /**
6567   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6568   * @param {Function} fn        The method the event invokes
6569   * @param {Object}   scope    An  object that becomes the scope of the handler
6570   * @param {boolean}  override If true, the obj passed in becomes
6571   *                             the execution scope of the listener
6572   * @member Roo
6573   * @method onReady
6574  */
6575 Roo.onReady = Roo.EventManager.onDocumentReady;
6576
6577 Roo.onReady(function(){
6578     var bd = Roo.get(document.body);
6579     if(!bd){ return; }
6580
6581     var cls = [
6582             Roo.isIE ? "roo-ie"
6583             : Roo.isGecko ? "roo-gecko"
6584             : Roo.isOpera ? "roo-opera"
6585             : Roo.isSafari ? "roo-safari" : ""];
6586
6587     if(Roo.isMac){
6588         cls.push("roo-mac");
6589     }
6590     if(Roo.isLinux){
6591         cls.push("roo-linux");
6592     }
6593     if(Roo.isIOS){
6594         cls.push("roo-ios");
6595     }
6596     if(Roo.isTouch){
6597         cls.push("roo-touch");
6598     }
6599     if(Roo.isBorderBox){
6600         cls.push('roo-border-box');
6601     }
6602     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6603         var p = bd.dom.parentNode;
6604         if(p){
6605             p.className += ' roo-strict';
6606         }
6607     }
6608     bd.addClass(cls.join(' '));
6609 });
6610
6611 /**
6612  * @class Roo.EventObject
6613  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6614  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6615  * Example:
6616  * <pre><code>
6617  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6618     e.preventDefault();
6619     var target = e.getTarget();
6620     ...
6621  }
6622  var myDiv = Roo.get("myDiv");
6623  myDiv.on("click", handleClick);
6624  //or
6625  Roo.EventManager.on("myDiv", 'click', handleClick);
6626  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6627  </code></pre>
6628  * @singleton
6629  */
6630 Roo.EventObject = function(){
6631     
6632     var E = Roo.lib.Event;
6633     
6634     // safari keypress events for special keys return bad keycodes
6635     var safariKeys = {
6636         63234 : 37, // left
6637         63235 : 39, // right
6638         63232 : 38, // up
6639         63233 : 40, // down
6640         63276 : 33, // page up
6641         63277 : 34, // page down
6642         63272 : 46, // delete
6643         63273 : 36, // home
6644         63275 : 35  // end
6645     };
6646
6647     // normalize button clicks
6648     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6649                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6650
6651     Roo.EventObjectImpl = function(e){
6652         if(e){
6653             this.setEvent(e.browserEvent || e);
6654         }
6655     };
6656     Roo.EventObjectImpl.prototype = {
6657         /**
6658          * Used to fix doc tools.
6659          * @scope Roo.EventObject.prototype
6660          */
6661             
6662
6663         
6664         
6665         /** The normal browser event */
6666         browserEvent : null,
6667         /** The button pressed in a mouse event */
6668         button : -1,
6669         /** True if the shift key was down during the event */
6670         shiftKey : false,
6671         /** True if the control key was down during the event */
6672         ctrlKey : false,
6673         /** True if the alt key was down during the event */
6674         altKey : false,
6675
6676         /** Key constant 
6677         * @type Number */
6678         BACKSPACE : 8,
6679         /** Key constant 
6680         * @type Number */
6681         TAB : 9,
6682         /** Key constant 
6683         * @type Number */
6684         RETURN : 13,
6685         /** Key constant 
6686         * @type Number */
6687         ENTER : 13,
6688         /** Key constant 
6689         * @type Number */
6690         SHIFT : 16,
6691         /** Key constant 
6692         * @type Number */
6693         CONTROL : 17,
6694         /** Key constant 
6695         * @type Number */
6696         ESC : 27,
6697         /** Key constant 
6698         * @type Number */
6699         SPACE : 32,
6700         /** Key constant 
6701         * @type Number */
6702         PAGEUP : 33,
6703         /** Key constant 
6704         * @type Number */
6705         PAGEDOWN : 34,
6706         /** Key constant 
6707         * @type Number */
6708         END : 35,
6709         /** Key constant 
6710         * @type Number */
6711         HOME : 36,
6712         /** Key constant 
6713         * @type Number */
6714         LEFT : 37,
6715         /** Key constant 
6716         * @type Number */
6717         UP : 38,
6718         /** Key constant 
6719         * @type Number */
6720         RIGHT : 39,
6721         /** Key constant 
6722         * @type Number */
6723         DOWN : 40,
6724         /** Key constant 
6725         * @type Number */
6726         DELETE : 46,
6727         /** Key constant 
6728         * @type Number */
6729         F5 : 116,
6730
6731            /** @private */
6732         setEvent : function(e){
6733             if(e == this || (e && e.browserEvent)){ // already wrapped
6734                 return e;
6735             }
6736             this.browserEvent = e;
6737             if(e){
6738                 // normalize buttons
6739                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6740                 if(e.type == 'click' && this.button == -1){
6741                     this.button = 0;
6742                 }
6743                 this.type = e.type;
6744                 this.shiftKey = e.shiftKey;
6745                 // mac metaKey behaves like ctrlKey
6746                 this.ctrlKey = e.ctrlKey || e.metaKey;
6747                 this.altKey = e.altKey;
6748                 // in getKey these will be normalized for the mac
6749                 this.keyCode = e.keyCode;
6750                 // keyup warnings on firefox.
6751                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6752                 // cache the target for the delayed and or buffered events
6753                 this.target = E.getTarget(e);
6754                 // same for XY
6755                 this.xy = E.getXY(e);
6756             }else{
6757                 this.button = -1;
6758                 this.shiftKey = false;
6759                 this.ctrlKey = false;
6760                 this.altKey = false;
6761                 this.keyCode = 0;
6762                 this.charCode =0;
6763                 this.target = null;
6764                 this.xy = [0, 0];
6765             }
6766             return this;
6767         },
6768
6769         /**
6770          * Stop the event (preventDefault and stopPropagation)
6771          */
6772         stopEvent : function(){
6773             if(this.browserEvent){
6774                 if(this.browserEvent.type == 'mousedown'){
6775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6776                 }
6777                 E.stopEvent(this.browserEvent);
6778             }
6779         },
6780
6781         /**
6782          * Prevents the browsers default handling of the event.
6783          */
6784         preventDefault : function(){
6785             if(this.browserEvent){
6786                 E.preventDefault(this.browserEvent);
6787             }
6788         },
6789
6790         /** @private */
6791         isNavKeyPress : function(){
6792             var k = this.keyCode;
6793             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6794             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6795         },
6796
6797         isSpecialKey : function(){
6798             var k = this.keyCode;
6799             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6800             (k == 16) || (k == 17) ||
6801             (k >= 18 && k <= 20) ||
6802             (k >= 33 && k <= 35) ||
6803             (k >= 36 && k <= 39) ||
6804             (k >= 44 && k <= 45);
6805         },
6806         /**
6807          * Cancels bubbling of the event.
6808          */
6809         stopPropagation : function(){
6810             if(this.browserEvent){
6811                 if(this.type == 'mousedown'){
6812                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6813                 }
6814                 E.stopPropagation(this.browserEvent);
6815             }
6816         },
6817
6818         /**
6819          * Gets the key code for the event.
6820          * @return {Number}
6821          */
6822         getCharCode : function(){
6823             return this.charCode || this.keyCode;
6824         },
6825
6826         /**
6827          * Returns a normalized keyCode for the event.
6828          * @return {Number} The key code
6829          */
6830         getKey : function(){
6831             var k = this.keyCode || this.charCode;
6832             return Roo.isSafari ? (safariKeys[k] || k) : k;
6833         },
6834
6835         /**
6836          * Gets the x coordinate of the event.
6837          * @return {Number}
6838          */
6839         getPageX : function(){
6840             return this.xy[0];
6841         },
6842
6843         /**
6844          * Gets the y coordinate of the event.
6845          * @return {Number}
6846          */
6847         getPageY : function(){
6848             return this.xy[1];
6849         },
6850
6851         /**
6852          * Gets the time of the event.
6853          * @return {Number}
6854          */
6855         getTime : function(){
6856             if(this.browserEvent){
6857                 return E.getTime(this.browserEvent);
6858             }
6859             return null;
6860         },
6861
6862         /**
6863          * Gets the page coordinates of the event.
6864          * @return {Array} The xy values like [x, y]
6865          */
6866         getXY : function(){
6867             return this.xy;
6868         },
6869
6870         /**
6871          * Gets the target for the event.
6872          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6873          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6874                 search as a number or element (defaults to 10 || document.body)
6875          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6876          * @return {HTMLelement}
6877          */
6878         getTarget : function(selector, maxDepth, returnEl){
6879             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6880         },
6881         /**
6882          * Gets the related target.
6883          * @return {HTMLElement}
6884          */
6885         getRelatedTarget : function(){
6886             if(this.browserEvent){
6887                 return E.getRelatedTarget(this.browserEvent);
6888             }
6889             return null;
6890         },
6891
6892         /**
6893          * Normalizes mouse wheel delta across browsers
6894          * @return {Number} The delta
6895          */
6896         getWheelDelta : function(){
6897             var e = this.browserEvent;
6898             var delta = 0;
6899             if(e.wheelDelta){ /* IE/Opera. */
6900                 delta = e.wheelDelta/120;
6901             }else if(e.detail){ /* Mozilla case. */
6902                 delta = -e.detail/3;
6903             }
6904             return delta;
6905         },
6906
6907         /**
6908          * Returns true if the control, meta, shift or alt key was pressed during this event.
6909          * @return {Boolean}
6910          */
6911         hasModifier : function(){
6912             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6913         },
6914
6915         /**
6916          * Returns true if the target of this event equals el or is a child of el
6917          * @param {String/HTMLElement/Element} el
6918          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6919          * @return {Boolean}
6920          */
6921         within : function(el, related){
6922             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6923             return t && Roo.fly(el).contains(t);
6924         },
6925
6926         getPoint : function(){
6927             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6928         }
6929     };
6930
6931     return new Roo.EventObjectImpl();
6932 }();
6933             
6934     /*
6935  * Based on:
6936  * Ext JS Library 1.1.1
6937  * Copyright(c) 2006-2007, Ext JS, LLC.
6938  *
6939  * Originally Released Under LGPL - original licence link has changed is not relivant.
6940  *
6941  * Fork - LGPL
6942  * <script type="text/javascript">
6943  */
6944
6945  
6946 // was in Composite Element!??!?!
6947  
6948 (function(){
6949     var D = Roo.lib.Dom;
6950     var E = Roo.lib.Event;
6951     var A = Roo.lib.Anim;
6952
6953     // local style camelizing for speed
6954     var propCache = {};
6955     var camelRe = /(-[a-z])/gi;
6956     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6957     var view = document.defaultView;
6958
6959 /**
6960  * @class Roo.Element
6961  * Represents an Element in the DOM.<br><br>
6962  * Usage:<br>
6963 <pre><code>
6964 var el = Roo.get("my-div");
6965
6966 // or with getEl
6967 var el = getEl("my-div");
6968
6969 // or with a DOM element
6970 var el = Roo.get(myDivElement);
6971 </code></pre>
6972  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6973  * each call instead of constructing a new one.<br><br>
6974  * <b>Animations</b><br />
6975  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6976  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6977 <pre>
6978 Option    Default   Description
6979 --------- --------  ---------------------------------------------
6980 duration  .35       The duration of the animation in seconds
6981 easing    easeOut   The YUI easing method
6982 callback  none      A function to execute when the anim completes
6983 scope     this      The scope (this) of the callback function
6984 </pre>
6985 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6986 * manipulate the animation. Here's an example:
6987 <pre><code>
6988 var el = Roo.get("my-div");
6989
6990 // no animation
6991 el.setWidth(100);
6992
6993 // default animation
6994 el.setWidth(100, true);
6995
6996 // animation with some options set
6997 el.setWidth(100, {
6998     duration: 1,
6999     callback: this.foo,
7000     scope: this
7001 });
7002
7003 // using the "anim" property to get the Anim object
7004 var opt = {
7005     duration: 1,
7006     callback: this.foo,
7007     scope: this
7008 };
7009 el.setWidth(100, opt);
7010 ...
7011 if(opt.anim.isAnimated()){
7012     opt.anim.stop();
7013 }
7014 </code></pre>
7015 * <b> Composite (Collections of) Elements</b><br />
7016  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7017  * @constructor Create a new Element directly.
7018  * @param {String/HTMLElement} element
7019  * @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).
7020  */
7021     Roo.Element = function(element, forceNew){
7022         var dom = typeof element == "string" ?
7023                 document.getElementById(element) : element;
7024         if(!dom){ // invalid id/element
7025             return null;
7026         }
7027         var id = dom.id;
7028         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7029             return Roo.Element.cache[id];
7030         }
7031
7032         /**
7033          * The DOM element
7034          * @type HTMLElement
7035          */
7036         this.dom = dom;
7037
7038         /**
7039          * The DOM element ID
7040          * @type String
7041          */
7042         this.id = id || Roo.id(dom);
7043     };
7044
7045     var El = Roo.Element;
7046
7047     El.prototype = {
7048         /**
7049          * The element's default display mode  (defaults to "")
7050          * @type String
7051          */
7052         originalDisplay : "",
7053
7054         visibilityMode : 1,
7055         /**
7056          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7057          * @type String
7058          */
7059         defaultUnit : "px",
7060         
7061         /**
7062          * Sets the element's visibility mode. When setVisible() is called it
7063          * will use this to determine whether to set the visibility or the display property.
7064          * @param visMode Element.VISIBILITY or Element.DISPLAY
7065          * @return {Roo.Element} this
7066          */
7067         setVisibilityMode : function(visMode){
7068             this.visibilityMode = visMode;
7069             return this;
7070         },
7071         /**
7072          * Convenience method for setVisibilityMode(Element.DISPLAY)
7073          * @param {String} display (optional) What to set display to when visible
7074          * @return {Roo.Element} this
7075          */
7076         enableDisplayMode : function(display){
7077             this.setVisibilityMode(El.DISPLAY);
7078             if(typeof display != "undefined") { this.originalDisplay = display; }
7079             return this;
7080         },
7081
7082         /**
7083          * 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)
7084          * @param {String} selector The simple selector to test
7085          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7086                 search as a number or element (defaults to 10 || document.body)
7087          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7088          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7089          */
7090         findParent : function(simpleSelector, maxDepth, returnEl){
7091             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7092             maxDepth = maxDepth || 50;
7093             if(typeof maxDepth != "number"){
7094                 stopEl = Roo.getDom(maxDepth);
7095                 maxDepth = 10;
7096             }
7097             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7098                 if(dq.is(p, simpleSelector)){
7099                     return returnEl ? Roo.get(p) : p;
7100                 }
7101                 depth++;
7102                 p = p.parentNode;
7103             }
7104             return null;
7105         },
7106
7107
7108         /**
7109          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7110          * @param {String} selector The simple selector to test
7111          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7112                 search as a number or element (defaults to 10 || document.body)
7113          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7114          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7115          */
7116         findParentNode : function(simpleSelector, maxDepth, returnEl){
7117             var p = Roo.fly(this.dom.parentNode, '_internal');
7118             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7119         },
7120
7121         /**
7122          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7123          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7124          * @param {String} selector The simple selector to test
7125          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7126                 search as a number or element (defaults to 10 || document.body)
7127          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7128          */
7129         up : function(simpleSelector, maxDepth){
7130             return this.findParentNode(simpleSelector, maxDepth, true);
7131         },
7132
7133
7134
7135         /**
7136          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7137          * @param {String} selector The simple selector to test
7138          * @return {Boolean} True if this element matches the selector, else false
7139          */
7140         is : function(simpleSelector){
7141             return Roo.DomQuery.is(this.dom, simpleSelector);
7142         },
7143
7144         /**
7145          * Perform animation on this element.
7146          * @param {Object} args The YUI animation control args
7147          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7148          * @param {Function} onComplete (optional) Function to call when animation completes
7149          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7150          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7151          * @return {Roo.Element} this
7152          */
7153         animate : function(args, duration, onComplete, easing, animType){
7154             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7155             return this;
7156         },
7157
7158         /*
7159          * @private Internal animation call
7160          */
7161         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7162             animType = animType || 'run';
7163             opt = opt || {};
7164             var anim = Roo.lib.Anim[animType](
7165                 this.dom, args,
7166                 (opt.duration || defaultDur) || .35,
7167                 (opt.easing || defaultEase) || 'easeOut',
7168                 function(){
7169                     Roo.callback(cb, this);
7170                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7171                 },
7172                 this
7173             );
7174             opt.anim = anim;
7175             return anim;
7176         },
7177
7178         // private legacy anim prep
7179         preanim : function(a, i){
7180             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7181         },
7182
7183         /**
7184          * Removes worthless text nodes
7185          * @param {Boolean} forceReclean (optional) By default the element
7186          * keeps track if it has been cleaned already so
7187          * you can call this over and over. However, if you update the element and
7188          * need to force a reclean, you can pass true.
7189          */
7190         clean : function(forceReclean){
7191             if(this.isCleaned && forceReclean !== true){
7192                 return this;
7193             }
7194             var ns = /\S/;
7195             var d = this.dom, n = d.firstChild, ni = -1;
7196             while(n){
7197                 var nx = n.nextSibling;
7198                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7199                     d.removeChild(n);
7200                 }else{
7201                     n.nodeIndex = ++ni;
7202                 }
7203                 n = nx;
7204             }
7205             this.isCleaned = true;
7206             return this;
7207         },
7208
7209         // private
7210         calcOffsetsTo : function(el){
7211             el = Roo.get(el);
7212             var d = el.dom;
7213             var restorePos = false;
7214             if(el.getStyle('position') == 'static'){
7215                 el.position('relative');
7216                 restorePos = true;
7217             }
7218             var x = 0, y =0;
7219             var op = this.dom;
7220             while(op && op != d && op.tagName != 'HTML'){
7221                 x+= op.offsetLeft;
7222                 y+= op.offsetTop;
7223                 op = op.offsetParent;
7224             }
7225             if(restorePos){
7226                 el.position('static');
7227             }
7228             return [x, y];
7229         },
7230
7231         /**
7232          * Scrolls this element into view within the passed container.
7233          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7234          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7235          * @return {Roo.Element} this
7236          */
7237         scrollIntoView : function(container, hscroll){
7238             var c = Roo.getDom(container) || document.body;
7239             var el = this.dom;
7240
7241             var o = this.calcOffsetsTo(c),
7242                 l = o[0],
7243                 t = o[1],
7244                 b = t+el.offsetHeight,
7245                 r = l+el.offsetWidth;
7246
7247             var ch = c.clientHeight;
7248             var ct = parseInt(c.scrollTop, 10);
7249             var cl = parseInt(c.scrollLeft, 10);
7250             var cb = ct + ch;
7251             var cr = cl + c.clientWidth;
7252
7253             if(t < ct){
7254                 c.scrollTop = t;
7255             }else if(b > cb){
7256                 c.scrollTop = b-ch;
7257             }
7258
7259             if(hscroll !== false){
7260                 if(l < cl){
7261                     c.scrollLeft = l;
7262                 }else if(r > cr){
7263                     c.scrollLeft = r-c.clientWidth;
7264                 }
7265             }
7266             return this;
7267         },
7268
7269         // private
7270         scrollChildIntoView : function(child, hscroll){
7271             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7272         },
7273
7274         /**
7275          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7276          * the new height may not be available immediately.
7277          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7278          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7279          * @param {Function} onComplete (optional) Function to call when animation completes
7280          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7281          * @return {Roo.Element} this
7282          */
7283         autoHeight : function(animate, duration, onComplete, easing){
7284             var oldHeight = this.getHeight();
7285             this.clip();
7286             this.setHeight(1); // force clipping
7287             setTimeout(function(){
7288                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7289                 if(!animate){
7290                     this.setHeight(height);
7291                     this.unclip();
7292                     if(typeof onComplete == "function"){
7293                         onComplete();
7294                     }
7295                 }else{
7296                     this.setHeight(oldHeight); // restore original height
7297                     this.setHeight(height, animate, duration, function(){
7298                         this.unclip();
7299                         if(typeof onComplete == "function") { onComplete(); }
7300                     }.createDelegate(this), easing);
7301                 }
7302             }.createDelegate(this), 0);
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if this element is an ancestor of the passed element
7308          * @param {HTMLElement/String} el The element to check
7309          * @return {Boolean} True if this element is an ancestor of el, else false
7310          */
7311         contains : function(el){
7312             if(!el){return false;}
7313             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7314         },
7315
7316         /**
7317          * Checks whether the element is currently visible using both visibility and display properties.
7318          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7319          * @return {Boolean} True if the element is currently visible, else false
7320          */
7321         isVisible : function(deep) {
7322             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7323             if(deep !== true || !vis){
7324                 return vis;
7325             }
7326             var p = this.dom.parentNode;
7327             while(p && p.tagName.toLowerCase() != "body"){
7328                 if(!Roo.fly(p, '_isVisible').isVisible()){
7329                     return false;
7330                 }
7331                 p = p.parentNode;
7332             }
7333             return true;
7334         },
7335
7336         /**
7337          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7338          * @param {String} selector The CSS selector
7339          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7340          * @return {CompositeElement/CompositeElementLite} The composite element
7341          */
7342         select : function(selector, unique){
7343             return El.select(selector, unique, this.dom);
7344         },
7345
7346         /**
7347          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7348          * @param {String} selector The CSS selector
7349          * @return {Array} An array of the matched nodes
7350          */
7351         query : function(selector, unique){
7352             return Roo.DomQuery.select(selector, this.dom);
7353         },
7354
7355         /**
7356          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7357          * @param {String} selector The CSS selector
7358          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7359          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7360          */
7361         child : function(selector, returnDom){
7362             var n = Roo.DomQuery.selectNode(selector, this.dom);
7363             return returnDom ? n : Roo.get(n);
7364         },
7365
7366         /**
7367          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7368          * @param {String} selector The CSS selector
7369          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7370          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7371          */
7372         down : function(selector, returnDom){
7373             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7374             return returnDom ? n : Roo.get(n);
7375         },
7376
7377         /**
7378          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7379          * @param {String} group The group the DD object is member of
7380          * @param {Object} config The DD config object
7381          * @param {Object} overrides An object containing methods to override/implement on the DD object
7382          * @return {Roo.dd.DD} The DD object
7383          */
7384         initDD : function(group, config, overrides){
7385             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7386             return Roo.apply(dd, overrides);
7387         },
7388
7389         /**
7390          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7391          * @param {String} group The group the DDProxy object is member of
7392          * @param {Object} config The DDProxy config object
7393          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7394          * @return {Roo.dd.DDProxy} The DDProxy object
7395          */
7396         initDDProxy : function(group, config, overrides){
7397             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7398             return Roo.apply(dd, overrides);
7399         },
7400
7401         /**
7402          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7403          * @param {String} group The group the DDTarget object is member of
7404          * @param {Object} config The DDTarget config object
7405          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7406          * @return {Roo.dd.DDTarget} The DDTarget object
7407          */
7408         initDDTarget : function(group, config, overrides){
7409             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7410             return Roo.apply(dd, overrides);
7411         },
7412
7413         /**
7414          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7415          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7416          * @param {Boolean} visible Whether the element is visible
7417          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7418          * @return {Roo.Element} this
7419          */
7420          setVisible : function(visible, animate){
7421             if(!animate || !A){
7422                 if(this.visibilityMode == El.DISPLAY){
7423                     this.setDisplayed(visible);
7424                 }else{
7425                     this.fixDisplay();
7426                     this.dom.style.visibility = visible ? "visible" : "hidden";
7427                 }
7428             }else{
7429                 // closure for composites
7430                 var dom = this.dom;
7431                 var visMode = this.visibilityMode;
7432                 if(visible){
7433                     this.setOpacity(.01);
7434                     this.setVisible(true);
7435                 }
7436                 this.anim({opacity: { to: (visible?1:0) }},
7437                       this.preanim(arguments, 1),
7438                       null, .35, 'easeIn', function(){
7439                          if(!visible){
7440                              if(visMode == El.DISPLAY){
7441                                  dom.style.display = "none";
7442                              }else{
7443                                  dom.style.visibility = "hidden";
7444                              }
7445                              Roo.get(dom).setOpacity(1);
7446                          }
7447                      });
7448             }
7449             return this;
7450         },
7451
7452         /**
7453          * Returns true if display is not "none"
7454          * @return {Boolean}
7455          */
7456         isDisplayed : function() {
7457             return this.getStyle("display") != "none";
7458         },
7459
7460         /**
7461          * Toggles the element's visibility or display, depending on visibility mode.
7462          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7463          * @return {Roo.Element} this
7464          */
7465         toggle : function(animate){
7466             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7467             return this;
7468         },
7469
7470         /**
7471          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7472          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7473          * @return {Roo.Element} this
7474          */
7475         setDisplayed : function(value) {
7476             if(typeof value == "boolean"){
7477                value = value ? this.originalDisplay : "none";
7478             }
7479             this.setStyle("display", value);
7480             return this;
7481         },
7482
7483         /**
7484          * Tries to focus the element. Any exceptions are caught and ignored.
7485          * @return {Roo.Element} this
7486          */
7487         focus : function() {
7488             try{
7489                 this.dom.focus();
7490             }catch(e){}
7491             return this;
7492         },
7493
7494         /**
7495          * Tries to blur the element. Any exceptions are caught and ignored.
7496          * @return {Roo.Element} this
7497          */
7498         blur : function() {
7499             try{
7500                 this.dom.blur();
7501             }catch(e){}
7502             return this;
7503         },
7504
7505         /**
7506          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7507          * @param {String/Array} className The CSS class to add, or an array of classes
7508          * @return {Roo.Element} this
7509          */
7510         addClass : function(className){
7511             if(className instanceof Array){
7512                 for(var i = 0, len = className.length; i < len; i++) {
7513                     this.addClass(className[i]);
7514                 }
7515             }else{
7516                 if(className && !this.hasClass(className)){
7517                     this.dom.className = this.dom.className + " " + className;
7518                 }
7519             }
7520             return this;
7521         },
7522
7523         /**
7524          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7525          * @param {String/Array} className The CSS class to add, or an array of classes
7526          * @return {Roo.Element} this
7527          */
7528         radioClass : function(className){
7529             var siblings = this.dom.parentNode.childNodes;
7530             for(var i = 0; i < siblings.length; i++) {
7531                 var s = siblings[i];
7532                 if(s.nodeType == 1){
7533                     Roo.get(s).removeClass(className);
7534                 }
7535             }
7536             this.addClass(className);
7537             return this;
7538         },
7539
7540         /**
7541          * Removes one or more CSS classes from the element.
7542          * @param {String/Array} className The CSS class to remove, or an array of classes
7543          * @return {Roo.Element} this
7544          */
7545         removeClass : function(className){
7546             if(!className || !this.dom.className){
7547                 return this;
7548             }
7549             if(className instanceof Array){
7550                 for(var i = 0, len = className.length; i < len; i++) {
7551                     this.removeClass(className[i]);
7552                 }
7553             }else{
7554                 if(this.hasClass(className)){
7555                     var re = this.classReCache[className];
7556                     if (!re) {
7557                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7558                        this.classReCache[className] = re;
7559                     }
7560                     this.dom.className =
7561                         this.dom.className.replace(re, " ");
7562                 }
7563             }
7564             return this;
7565         },
7566
7567         // private
7568         classReCache: {},
7569
7570         /**
7571          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7572          * @param {String} className The CSS class to toggle
7573          * @return {Roo.Element} this
7574          */
7575         toggleClass : function(className){
7576             if(this.hasClass(className)){
7577                 this.removeClass(className);
7578             }else{
7579                 this.addClass(className);
7580             }
7581             return this;
7582         },
7583
7584         /**
7585          * Checks if the specified CSS class exists on this element's DOM node.
7586          * @param {String} className The CSS class to check for
7587          * @return {Boolean} True if the class exists, else false
7588          */
7589         hasClass : function(className){
7590             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7591         },
7592
7593         /**
7594          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7595          * @param {String} oldClassName The CSS class to replace
7596          * @param {String} newClassName The replacement CSS class
7597          * @return {Roo.Element} this
7598          */
7599         replaceClass : function(oldClassName, newClassName){
7600             this.removeClass(oldClassName);
7601             this.addClass(newClassName);
7602             return this;
7603         },
7604
7605         /**
7606          * Returns an object with properties matching the styles requested.
7607          * For example, el.getStyles('color', 'font-size', 'width') might return
7608          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7609          * @param {String} style1 A style name
7610          * @param {String} style2 A style name
7611          * @param {String} etc.
7612          * @return {Object} The style object
7613          */
7614         getStyles : function(){
7615             var a = arguments, len = a.length, r = {};
7616             for(var i = 0; i < len; i++){
7617                 r[a[i]] = this.getStyle(a[i]);
7618             }
7619             return r;
7620         },
7621
7622         /**
7623          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7624          * @param {String} property The style property whose value is returned.
7625          * @return {String} The current value of the style property for this element.
7626          */
7627         getStyle : function(){
7628             return view && view.getComputedStyle ?
7629                 function(prop){
7630                     var el = this.dom, v, cs, camel;
7631                     if(prop == 'float'){
7632                         prop = "cssFloat";
7633                     }
7634                     if(el.style && (v = el.style[prop])){
7635                         return v;
7636                     }
7637                     if(cs = view.getComputedStyle(el, "")){
7638                         if(!(camel = propCache[prop])){
7639                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7640                         }
7641                         return cs[camel];
7642                     }
7643                     return null;
7644                 } :
7645                 function(prop){
7646                     var el = this.dom, v, cs, camel;
7647                     if(prop == 'opacity'){
7648                         if(typeof el.style.filter == 'string'){
7649                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7650                             if(m){
7651                                 var fv = parseFloat(m[1]);
7652                                 if(!isNaN(fv)){
7653                                     return fv ? fv / 100 : 0;
7654                                 }
7655                             }
7656                         }
7657                         return 1;
7658                     }else if(prop == 'float'){
7659                         prop = "styleFloat";
7660                     }
7661                     if(!(camel = propCache[prop])){
7662                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7663                     }
7664                     if(v = el.style[camel]){
7665                         return v;
7666                     }
7667                     if(cs = el.currentStyle){
7668                         return cs[camel];
7669                     }
7670                     return null;
7671                 };
7672         }(),
7673
7674         /**
7675          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7676          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7677          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7678          * @return {Roo.Element} this
7679          */
7680         setStyle : function(prop, value){
7681             if(typeof prop == "string"){
7682                 
7683                 if (prop == 'float') {
7684                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7685                     return this;
7686                 }
7687                 
7688                 var camel;
7689                 if(!(camel = propCache[prop])){
7690                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7691                 }
7692                 
7693                 if(camel == 'opacity') {
7694                     this.setOpacity(value);
7695                 }else{
7696                     this.dom.style[camel] = value;
7697                 }
7698             }else{
7699                 for(var style in prop){
7700                     if(typeof prop[style] != "function"){
7701                        this.setStyle(style, prop[style]);
7702                     }
7703                 }
7704             }
7705             return this;
7706         },
7707
7708         /**
7709          * More flexible version of {@link #setStyle} for setting style properties.
7710          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7711          * a function which returns such a specification.
7712          * @return {Roo.Element} this
7713          */
7714         applyStyles : function(style){
7715             Roo.DomHelper.applyStyles(this.dom, style);
7716             return this;
7717         },
7718
7719         /**
7720           * 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).
7721           * @return {Number} The X position of the element
7722           */
7723         getX : function(){
7724             return D.getX(this.dom);
7725         },
7726
7727         /**
7728           * 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).
7729           * @return {Number} The Y position of the element
7730           */
7731         getY : function(){
7732             return D.getY(this.dom);
7733         },
7734
7735         /**
7736           * 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).
7737           * @return {Array} The XY position of the element
7738           */
7739         getXY : function(){
7740             return D.getXY(this.dom);
7741         },
7742
7743         /**
7744          * 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).
7745          * @param {Number} The X position of the element
7746          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setX : function(x, animate){
7750             if(!animate || !A){
7751                 D.setX(this.dom, x);
7752             }else{
7753                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7754             }
7755             return this;
7756         },
7757
7758         /**
7759          * 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).
7760          * @param {Number} The Y position of the element
7761          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7762          * @return {Roo.Element} this
7763          */
7764         setY : function(y, animate){
7765             if(!animate || !A){
7766                 D.setY(this.dom, y);
7767             }else{
7768                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7769             }
7770             return this;
7771         },
7772
7773         /**
7774          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7775          * @param {String} left The left CSS property value
7776          * @return {Roo.Element} this
7777          */
7778         setLeft : function(left){
7779             this.setStyle("left", this.addUnits(left));
7780             return this;
7781         },
7782
7783         /**
7784          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7785          * @param {String} top The top CSS property value
7786          * @return {Roo.Element} this
7787          */
7788         setTop : function(top){
7789             this.setStyle("top", this.addUnits(top));
7790             return this;
7791         },
7792
7793         /**
7794          * Sets the element's CSS right style.
7795          * @param {String} right The right CSS property value
7796          * @return {Roo.Element} this
7797          */
7798         setRight : function(right){
7799             this.setStyle("right", this.addUnits(right));
7800             return this;
7801         },
7802
7803         /**
7804          * Sets the element's CSS bottom style.
7805          * @param {String} bottom The bottom CSS property value
7806          * @return {Roo.Element} this
7807          */
7808         setBottom : function(bottom){
7809             this.setStyle("bottom", this.addUnits(bottom));
7810             return this;
7811         },
7812
7813         /**
7814          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7815          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7816          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7817          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7818          * @return {Roo.Element} this
7819          */
7820         setXY : function(pos, animate){
7821             if(!animate || !A){
7822                 D.setXY(this.dom, pos);
7823             }else{
7824                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7825             }
7826             return this;
7827         },
7828
7829         /**
7830          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7831          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7832          * @param {Number} x X value for new position (coordinates are page-based)
7833          * @param {Number} y Y value for new position (coordinates are page-based)
7834          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7835          * @return {Roo.Element} this
7836          */
7837         setLocation : function(x, y, animate){
7838             this.setXY([x, y], this.preanim(arguments, 2));
7839             return this;
7840         },
7841
7842         /**
7843          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7844          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7845          * @param {Number} x X value for new position (coordinates are page-based)
7846          * @param {Number} y Y value for new position (coordinates are page-based)
7847          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7848          * @return {Roo.Element} this
7849          */
7850         moveTo : function(x, y, animate){
7851             this.setXY([x, y], this.preanim(arguments, 2));
7852             return this;
7853         },
7854
7855         /**
7856          * Returns the region of the given element.
7857          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7858          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7859          */
7860         getRegion : function(){
7861             return D.getRegion(this.dom);
7862         },
7863
7864         /**
7865          * Returns the offset height of the element
7866          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7867          * @return {Number} The element's height
7868          */
7869         getHeight : function(contentHeight){
7870             var h = this.dom.offsetHeight || 0;
7871             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7872         },
7873
7874         /**
7875          * Returns the offset width of the element
7876          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7877          * @return {Number} The element's width
7878          */
7879         getWidth : function(contentWidth){
7880             var w = this.dom.offsetWidth || 0;
7881             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7882         },
7883
7884         /**
7885          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7886          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7887          * if a height has not been set using CSS.
7888          * @return {Number}
7889          */
7890         getComputedHeight : function(){
7891             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7892             if(!h){
7893                 h = parseInt(this.getStyle('height'), 10) || 0;
7894                 if(!this.isBorderBox()){
7895                     h += this.getFrameWidth('tb');
7896                 }
7897             }
7898             return h;
7899         },
7900
7901         /**
7902          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7903          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7904          * if a width has not been set using CSS.
7905          * @return {Number}
7906          */
7907         getComputedWidth : function(){
7908             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7909             if(!w){
7910                 w = parseInt(this.getStyle('width'), 10) || 0;
7911                 if(!this.isBorderBox()){
7912                     w += this.getFrameWidth('lr');
7913                 }
7914             }
7915             return w;
7916         },
7917
7918         /**
7919          * Returns the size of the element.
7920          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7921          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7922          */
7923         getSize : function(contentSize){
7924             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7925         },
7926
7927         /**
7928          * Returns the width and height of the viewport.
7929          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7930          */
7931         getViewSize : function(){
7932             var d = this.dom, doc = document, aw = 0, ah = 0;
7933             if(d == doc || d == doc.body){
7934                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7935             }else{
7936                 return {
7937                     width : d.clientWidth,
7938                     height: d.clientHeight
7939                 };
7940             }
7941         },
7942
7943         /**
7944          * Returns the value of the "value" attribute
7945          * @param {Boolean} asNumber true to parse the value as a number
7946          * @return {String/Number}
7947          */
7948         getValue : function(asNumber){
7949             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7950         },
7951
7952         // private
7953         adjustWidth : function(width){
7954             if(typeof width == "number"){
7955                 if(this.autoBoxAdjust && !this.isBorderBox()){
7956                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7957                 }
7958                 if(width < 0){
7959                     width = 0;
7960                 }
7961             }
7962             return width;
7963         },
7964
7965         // private
7966         adjustHeight : function(height){
7967             if(typeof height == "number"){
7968                if(this.autoBoxAdjust && !this.isBorderBox()){
7969                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7970                }
7971                if(height < 0){
7972                    height = 0;
7973                }
7974             }
7975             return height;
7976         },
7977
7978         /**
7979          * Set the width of the element
7980          * @param {Number} width The new width
7981          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7982          * @return {Roo.Element} this
7983          */
7984         setWidth : function(width, animate){
7985             width = this.adjustWidth(width);
7986             if(!animate || !A){
7987                 this.dom.style.width = this.addUnits(width);
7988             }else{
7989                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7990             }
7991             return this;
7992         },
7993
7994         /**
7995          * Set the height of the element
7996          * @param {Number} height The new height
7997          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7998          * @return {Roo.Element} this
7999          */
8000          setHeight : function(height, animate){
8001             height = this.adjustHeight(height);
8002             if(!animate || !A){
8003                 this.dom.style.height = this.addUnits(height);
8004             }else{
8005                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8006             }
8007             return this;
8008         },
8009
8010         /**
8011          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8012          * @param {Number} width The new width
8013          * @param {Number} height The new height
8014          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8015          * @return {Roo.Element} this
8016          */
8017          setSize : function(width, height, animate){
8018             if(typeof width == "object"){ // in case of object from getSize()
8019                 height = width.height; width = width.width;
8020             }
8021             width = this.adjustWidth(width); height = this.adjustHeight(height);
8022             if(!animate || !A){
8023                 this.dom.style.width = this.addUnits(width);
8024                 this.dom.style.height = this.addUnits(height);
8025             }else{
8026                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8027             }
8028             return this;
8029         },
8030
8031         /**
8032          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8033          * @param {Number} x X value for new position (coordinates are page-based)
8034          * @param {Number} y Y value for new position (coordinates are page-based)
8035          * @param {Number} width The new width
8036          * @param {Number} height The new height
8037          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8038          * @return {Roo.Element} this
8039          */
8040         setBounds : function(x, y, width, height, animate){
8041             if(!animate || !A){
8042                 this.setSize(width, height);
8043                 this.setLocation(x, y);
8044             }else{
8045                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8046                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8047                               this.preanim(arguments, 4), 'motion');
8048             }
8049             return this;
8050         },
8051
8052         /**
8053          * 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.
8054          * @param {Roo.lib.Region} region The region to fill
8055          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8056          * @return {Roo.Element} this
8057          */
8058         setRegion : function(region, animate){
8059             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8060             return this;
8061         },
8062
8063         /**
8064          * Appends an event handler
8065          *
8066          * @param {String}   eventName     The type of event to append
8067          * @param {Function} fn        The method the event invokes
8068          * @param {Object} scope       (optional) The scope (this object) of the fn
8069          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8070          */
8071         addListener : function(eventName, fn, scope, options){
8072             if (this.dom) {
8073                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8074             }
8075         },
8076
8077         /**
8078          * Removes an event handler from this element
8079          * @param {String} eventName the type of event to remove
8080          * @param {Function} fn the method the event invokes
8081          * @return {Roo.Element} this
8082          */
8083         removeListener : function(eventName, fn){
8084             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8085             return this;
8086         },
8087
8088         /**
8089          * Removes all previous added listeners from this element
8090          * @return {Roo.Element} this
8091          */
8092         removeAllListeners : function(){
8093             E.purgeElement(this.dom);
8094             return this;
8095         },
8096
8097         relayEvent : function(eventName, observable){
8098             this.on(eventName, function(e){
8099                 observable.fireEvent(eventName, e);
8100             });
8101         },
8102
8103         /**
8104          * Set the opacity of the element
8105          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8106          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8107          * @return {Roo.Element} this
8108          */
8109          setOpacity : function(opacity, animate){
8110             if(!animate || !A){
8111                 var s = this.dom.style;
8112                 if(Roo.isIE){
8113                     s.zoom = 1;
8114                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8115                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8116                 }else{
8117                     s.opacity = opacity;
8118                 }
8119             }else{
8120                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8121             }
8122             return this;
8123         },
8124
8125         /**
8126          * Gets the left X coordinate
8127          * @param {Boolean} local True to get the local css position instead of page coordinate
8128          * @return {Number}
8129          */
8130         getLeft : function(local){
8131             if(!local){
8132                 return this.getX();
8133             }else{
8134                 return parseInt(this.getStyle("left"), 10) || 0;
8135             }
8136         },
8137
8138         /**
8139          * Gets the right X coordinate of the element (element X position + element width)
8140          * @param {Boolean} local True to get the local css position instead of page coordinate
8141          * @return {Number}
8142          */
8143         getRight : function(local){
8144             if(!local){
8145                 return this.getX() + this.getWidth();
8146             }else{
8147                 return (this.getLeft(true) + this.getWidth()) || 0;
8148             }
8149         },
8150
8151         /**
8152          * Gets the top Y coordinate
8153          * @param {Boolean} local True to get the local css position instead of page coordinate
8154          * @return {Number}
8155          */
8156         getTop : function(local) {
8157             if(!local){
8158                 return this.getY();
8159             }else{
8160                 return parseInt(this.getStyle("top"), 10) || 0;
8161             }
8162         },
8163
8164         /**
8165          * Gets the bottom Y coordinate of the element (element Y position + element height)
8166          * @param {Boolean} local True to get the local css position instead of page coordinate
8167          * @return {Number}
8168          */
8169         getBottom : function(local){
8170             if(!local){
8171                 return this.getY() + this.getHeight();
8172             }else{
8173                 return (this.getTop(true) + this.getHeight()) || 0;
8174             }
8175         },
8176
8177         /**
8178         * Initializes positioning on this element. If a desired position is not passed, it will make the
8179         * the element positioned relative IF it is not already positioned.
8180         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8181         * @param {Number} zIndex (optional) The zIndex to apply
8182         * @param {Number} x (optional) Set the page X position
8183         * @param {Number} y (optional) Set the page Y position
8184         */
8185         position : function(pos, zIndex, x, y){
8186             if(!pos){
8187                if(this.getStyle('position') == 'static'){
8188                    this.setStyle('position', 'relative');
8189                }
8190             }else{
8191                 this.setStyle("position", pos);
8192             }
8193             if(zIndex){
8194                 this.setStyle("z-index", zIndex);
8195             }
8196             if(x !== undefined && y !== undefined){
8197                 this.setXY([x, y]);
8198             }else if(x !== undefined){
8199                 this.setX(x);
8200             }else if(y !== undefined){
8201                 this.setY(y);
8202             }
8203         },
8204
8205         /**
8206         * Clear positioning back to the default when the document was loaded
8207         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8208         * @return {Roo.Element} this
8209          */
8210         clearPositioning : function(value){
8211             value = value ||'';
8212             this.setStyle({
8213                 "left": value,
8214                 "right": value,
8215                 "top": value,
8216                 "bottom": value,
8217                 "z-index": "",
8218                 "position" : "static"
8219             });
8220             return this;
8221         },
8222
8223         /**
8224         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8225         * snapshot before performing an update and then restoring the element.
8226         * @return {Object}
8227         */
8228         getPositioning : function(){
8229             var l = this.getStyle("left");
8230             var t = this.getStyle("top");
8231             return {
8232                 "position" : this.getStyle("position"),
8233                 "left" : l,
8234                 "right" : l ? "" : this.getStyle("right"),
8235                 "top" : t,
8236                 "bottom" : t ? "" : this.getStyle("bottom"),
8237                 "z-index" : this.getStyle("z-index")
8238             };
8239         },
8240
8241         /**
8242          * Gets the width of the border(s) for the specified side(s)
8243          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8244          * passing lr would get the border (l)eft width + the border (r)ight width.
8245          * @return {Number} The width of the sides passed added together
8246          */
8247         getBorderWidth : function(side){
8248             return this.addStyles(side, El.borders);
8249         },
8250
8251         /**
8252          * Gets the width of the padding(s) for the specified side(s)
8253          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8254          * passing lr would get the padding (l)eft + the padding (r)ight.
8255          * @return {Number} The padding of the sides passed added together
8256          */
8257         getPadding : function(side){
8258             return this.addStyles(side, El.paddings);
8259         },
8260
8261         /**
8262         * Set positioning with an object returned by getPositioning().
8263         * @param {Object} posCfg
8264         * @return {Roo.Element} this
8265          */
8266         setPositioning : function(pc){
8267             this.applyStyles(pc);
8268             if(pc.right == "auto"){
8269                 this.dom.style.right = "";
8270             }
8271             if(pc.bottom == "auto"){
8272                 this.dom.style.bottom = "";
8273             }
8274             return this;
8275         },
8276
8277         // private
8278         fixDisplay : function(){
8279             if(this.getStyle("display") == "none"){
8280                 this.setStyle("visibility", "hidden");
8281                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8282                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8283                     this.setStyle("display", "block");
8284                 }
8285             }
8286         },
8287
8288         /**
8289          * Quick set left and top adding default units
8290          * @param {String} left The left CSS property value
8291          * @param {String} top The top CSS property value
8292          * @return {Roo.Element} this
8293          */
8294          setLeftTop : function(left, top){
8295             this.dom.style.left = this.addUnits(left);
8296             this.dom.style.top = this.addUnits(top);
8297             return this;
8298         },
8299
8300         /**
8301          * Move this element relative to its current position.
8302          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8303          * @param {Number} distance How far to move the element in pixels
8304          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8305          * @return {Roo.Element} this
8306          */
8307          move : function(direction, distance, animate){
8308             var xy = this.getXY();
8309             direction = direction.toLowerCase();
8310             switch(direction){
8311                 case "l":
8312                 case "left":
8313                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8314                     break;
8315                case "r":
8316                case "right":
8317                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8318                     break;
8319                case "t":
8320                case "top":
8321                case "up":
8322                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8323                     break;
8324                case "b":
8325                case "bottom":
8326                case "down":
8327                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8328                     break;
8329             }
8330             return this;
8331         },
8332
8333         /**
8334          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8335          * @return {Roo.Element} this
8336          */
8337         clip : function(){
8338             if(!this.isClipped){
8339                this.isClipped = true;
8340                this.originalClip = {
8341                    "o": this.getStyle("overflow"),
8342                    "x": this.getStyle("overflow-x"),
8343                    "y": this.getStyle("overflow-y")
8344                };
8345                this.setStyle("overflow", "hidden");
8346                this.setStyle("overflow-x", "hidden");
8347                this.setStyle("overflow-y", "hidden");
8348             }
8349             return this;
8350         },
8351
8352         /**
8353          *  Return clipping (overflow) to original clipping before clip() was called
8354          * @return {Roo.Element} this
8355          */
8356         unclip : function(){
8357             if(this.isClipped){
8358                 this.isClipped = false;
8359                 var o = this.originalClip;
8360                 if(o.o){this.setStyle("overflow", o.o);}
8361                 if(o.x){this.setStyle("overflow-x", o.x);}
8362                 if(o.y){this.setStyle("overflow-y", o.y);}
8363             }
8364             return this;
8365         },
8366
8367
8368         /**
8369          * Gets the x,y coordinates specified by the anchor position on the element.
8370          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8371          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8372          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8373          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8374          * @return {Array} [x, y] An array containing the element's x and y coordinates
8375          */
8376         getAnchorXY : function(anchor, local, s){
8377             //Passing a different size is useful for pre-calculating anchors,
8378             //especially for anchored animations that change the el size.
8379
8380             var w, h, vp = false;
8381             if(!s){
8382                 var d = this.dom;
8383                 if(d == document.body || d == document){
8384                     vp = true;
8385                     w = D.getViewWidth(); h = D.getViewHeight();
8386                 }else{
8387                     w = this.getWidth(); h = this.getHeight();
8388                 }
8389             }else{
8390                 w = s.width;  h = s.height;
8391             }
8392             var x = 0, y = 0, r = Math.round;
8393             switch((anchor || "tl").toLowerCase()){
8394                 case "c":
8395                     x = r(w*.5);
8396                     y = r(h*.5);
8397                 break;
8398                 case "t":
8399                     x = r(w*.5);
8400                     y = 0;
8401                 break;
8402                 case "l":
8403                     x = 0;
8404                     y = r(h*.5);
8405                 break;
8406                 case "r":
8407                     x = w;
8408                     y = r(h*.5);
8409                 break;
8410                 case "b":
8411                     x = r(w*.5);
8412                     y = h;
8413                 break;
8414                 case "tl":
8415                     x = 0;
8416                     y = 0;
8417                 break;
8418                 case "bl":
8419                     x = 0;
8420                     y = h;
8421                 break;
8422                 case "br":
8423                     x = w;
8424                     y = h;
8425                 break;
8426                 case "tr":
8427                     x = w;
8428                     y = 0;
8429                 break;
8430             }
8431             if(local === true){
8432                 return [x, y];
8433             }
8434             if(vp){
8435                 var sc = this.getScroll();
8436                 return [x + sc.left, y + sc.top];
8437             }
8438             //Add the element's offset xy
8439             var o = this.getXY();
8440             return [x+o[0], y+o[1]];
8441         },
8442
8443         /**
8444          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8445          * supported position values.
8446          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8447          * @param {String} position The position to align to.
8448          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8449          * @return {Array} [x, y]
8450          */
8451         getAlignToXY : function(el, p, o){
8452             el = Roo.get(el);
8453             var d = this.dom;
8454             if(!el.dom){
8455                 throw "Element.alignTo with an element that doesn't exist";
8456             }
8457             var c = false; //constrain to viewport
8458             var p1 = "", p2 = "";
8459             o = o || [0,0];
8460
8461             if(!p){
8462                 p = "tl-bl";
8463             }else if(p == "?"){
8464                 p = "tl-bl?";
8465             }else if(p.indexOf("-") == -1){
8466                 p = "tl-" + p;
8467             }
8468             p = p.toLowerCase();
8469             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8470             if(!m){
8471                throw "Element.alignTo with an invalid alignment " + p;
8472             }
8473             p1 = m[1]; p2 = m[2]; c = !!m[3];
8474
8475             //Subtract the aligned el's internal xy from the target's offset xy
8476             //plus custom offset to get the aligned el's new offset xy
8477             var a1 = this.getAnchorXY(p1, true);
8478             var a2 = el.getAnchorXY(p2, false);
8479             var x = a2[0] - a1[0] + o[0];
8480             var y = a2[1] - a1[1] + o[1];
8481             if(c){
8482                 //constrain the aligned el to viewport if necessary
8483                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8484                 // 5px of margin for ie
8485                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8486
8487                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8488                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8489                 //otherwise swap the aligned el to the opposite border of the target.
8490                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8491                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8492                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8493                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8494
8495                var doc = document;
8496                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8497                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8498
8499                if((x+w) > dw + scrollX){
8500                     x = swapX ? r.left-w : dw+scrollX-w;
8501                 }
8502                if(x < scrollX){
8503                    x = swapX ? r.right : scrollX;
8504                }
8505                if((y+h) > dh + scrollY){
8506                     y = swapY ? r.top-h : dh+scrollY-h;
8507                 }
8508                if (y < scrollY){
8509                    y = swapY ? r.bottom : scrollY;
8510                }
8511             }
8512             return [x,y];
8513         },
8514
8515         // private
8516         getConstrainToXY : function(){
8517             var os = {top:0, left:0, bottom:0, right: 0};
8518
8519             return function(el, local, offsets, proposedXY){
8520                 el = Roo.get(el);
8521                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8522
8523                 var vw, vh, vx = 0, vy = 0;
8524                 if(el.dom == document.body || el.dom == document){
8525                     vw = Roo.lib.Dom.getViewWidth();
8526                     vh = Roo.lib.Dom.getViewHeight();
8527                 }else{
8528                     vw = el.dom.clientWidth;
8529                     vh = el.dom.clientHeight;
8530                     if(!local){
8531                         var vxy = el.getXY();
8532                         vx = vxy[0];
8533                         vy = vxy[1];
8534                     }
8535                 }
8536
8537                 var s = el.getScroll();
8538
8539                 vx += offsets.left + s.left;
8540                 vy += offsets.top + s.top;
8541
8542                 vw -= offsets.right;
8543                 vh -= offsets.bottom;
8544
8545                 var vr = vx+vw;
8546                 var vb = vy+vh;
8547
8548                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8549                 var x = xy[0], y = xy[1];
8550                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8551
8552                 // only move it if it needs it
8553                 var moved = false;
8554
8555                 // first validate right/bottom
8556                 if((x + w) > vr){
8557                     x = vr - w;
8558                     moved = true;
8559                 }
8560                 if((y + h) > vb){
8561                     y = vb - h;
8562                     moved = true;
8563                 }
8564                 // then make sure top/left isn't negative
8565                 if(x < vx){
8566                     x = vx;
8567                     moved = true;
8568                 }
8569                 if(y < vy){
8570                     y = vy;
8571                     moved = true;
8572                 }
8573                 return moved ? [x, y] : false;
8574             };
8575         }(),
8576
8577         // private
8578         adjustForConstraints : function(xy, parent, offsets){
8579             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8580         },
8581
8582         /**
8583          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8584          * document it aligns it to the viewport.
8585          * The position parameter is optional, and can be specified in any one of the following formats:
8586          * <ul>
8587          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8588          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8589          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8590          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8591          *   <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
8592          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8593          * </ul>
8594          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8595          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8596          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8597          * that specified in order to enforce the viewport constraints.
8598          * Following are all of the supported anchor positions:
8599     <pre>
8600     Value  Description
8601     -----  -----------------------------
8602     tl     The top left corner (default)
8603     t      The center of the top edge
8604     tr     The top right corner
8605     l      The center of the left edge
8606     c      In the center of the element
8607     r      The center of the right edge
8608     bl     The bottom left corner
8609     b      The center of the bottom edge
8610     br     The bottom right corner
8611     </pre>
8612     Example Usage:
8613     <pre><code>
8614     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8615     el.alignTo("other-el");
8616
8617     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8618     el.alignTo("other-el", "tr?");
8619
8620     // align the bottom right corner of el with the center left edge of other-el
8621     el.alignTo("other-el", "br-l?");
8622
8623     // align the center of el with the bottom left corner of other-el and
8624     // adjust the x position by -6 pixels (and the y position by 0)
8625     el.alignTo("other-el", "c-bl", [-6, 0]);
8626     </code></pre>
8627          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8628          * @param {String} position The position to align to.
8629          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8630          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8631          * @return {Roo.Element} this
8632          */
8633         alignTo : function(element, position, offsets, animate){
8634             var xy = this.getAlignToXY(element, position, offsets);
8635             this.setXY(xy, this.preanim(arguments, 3));
8636             return this;
8637         },
8638
8639         /**
8640          * Anchors an element to another element and realigns it when the window is resized.
8641          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8642          * @param {String} position The position to align to.
8643          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8644          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8645          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8646          * is a number, it is used as the buffer delay (defaults to 50ms).
8647          * @param {Function} callback The function to call after the animation finishes
8648          * @return {Roo.Element} this
8649          */
8650         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8651             var action = function(){
8652                 this.alignTo(el, alignment, offsets, animate);
8653                 Roo.callback(callback, this);
8654             };
8655             Roo.EventManager.onWindowResize(action, this);
8656             var tm = typeof monitorScroll;
8657             if(tm != 'undefined'){
8658                 Roo.EventManager.on(window, 'scroll', action, this,
8659                     {buffer: tm == 'number' ? monitorScroll : 50});
8660             }
8661             action.call(this); // align immediately
8662             return this;
8663         },
8664         /**
8665          * Clears any opacity settings from this element. Required in some cases for IE.
8666          * @return {Roo.Element} this
8667          */
8668         clearOpacity : function(){
8669             if (window.ActiveXObject) {
8670                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8671                     this.dom.style.filter = "";
8672                 }
8673             } else {
8674                 this.dom.style.opacity = "";
8675                 this.dom.style["-moz-opacity"] = "";
8676                 this.dom.style["-khtml-opacity"] = "";
8677             }
8678             return this;
8679         },
8680
8681         /**
8682          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8683          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8684          * @return {Roo.Element} this
8685          */
8686         hide : function(animate){
8687             this.setVisible(false, this.preanim(arguments, 0));
8688             return this;
8689         },
8690
8691         /**
8692         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8693         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8694          * @return {Roo.Element} this
8695          */
8696         show : function(animate){
8697             this.setVisible(true, this.preanim(arguments, 0));
8698             return this;
8699         },
8700
8701         /**
8702          * @private Test if size has a unit, otherwise appends the default
8703          */
8704         addUnits : function(size){
8705             return Roo.Element.addUnits(size, this.defaultUnit);
8706         },
8707
8708         /**
8709          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8710          * @return {Roo.Element} this
8711          */
8712         beginMeasure : function(){
8713             var el = this.dom;
8714             if(el.offsetWidth || el.offsetHeight){
8715                 return this; // offsets work already
8716             }
8717             var changed = [];
8718             var p = this.dom, b = document.body; // start with this element
8719             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8720                 var pe = Roo.get(p);
8721                 if(pe.getStyle('display') == 'none'){
8722                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8723                     p.style.visibility = "hidden";
8724                     p.style.display = "block";
8725                 }
8726                 p = p.parentNode;
8727             }
8728             this._measureChanged = changed;
8729             return this;
8730
8731         },
8732
8733         /**
8734          * Restores displays to before beginMeasure was called
8735          * @return {Roo.Element} this
8736          */
8737         endMeasure : function(){
8738             var changed = this._measureChanged;
8739             if(changed){
8740                 for(var i = 0, len = changed.length; i < len; i++) {
8741                     var r = changed[i];
8742                     r.el.style.visibility = r.visibility;
8743                     r.el.style.display = "none";
8744                 }
8745                 this._measureChanged = null;
8746             }
8747             return this;
8748         },
8749
8750         /**
8751         * Update the innerHTML of this element, optionally searching for and processing scripts
8752         * @param {String} html The new HTML
8753         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8754         * @param {Function} callback For async script loading you can be noticed when the update completes
8755         * @return {Roo.Element} this
8756          */
8757         update : function(html, loadScripts, callback){
8758             if(typeof html == "undefined"){
8759                 html = "";
8760             }
8761             if(loadScripts !== true){
8762                 this.dom.innerHTML = html;
8763                 if(typeof callback == "function"){
8764                     callback();
8765                 }
8766                 return this;
8767             }
8768             var id = Roo.id();
8769             var dom = this.dom;
8770
8771             html += '<span id="' + id + '"></span>';
8772
8773             E.onAvailable(id, function(){
8774                 var hd = document.getElementsByTagName("head")[0];
8775                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8776                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8777                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8778
8779                 var match;
8780                 while(match = re.exec(html)){
8781                     var attrs = match[1];
8782                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8783                     if(srcMatch && srcMatch[2]){
8784                        var s = document.createElement("script");
8785                        s.src = srcMatch[2];
8786                        var typeMatch = attrs.match(typeRe);
8787                        if(typeMatch && typeMatch[2]){
8788                            s.type = typeMatch[2];
8789                        }
8790                        hd.appendChild(s);
8791                     }else if(match[2] && match[2].length > 0){
8792                         if(window.execScript) {
8793                            window.execScript(match[2]);
8794                         } else {
8795                             /**
8796                              * eval:var:id
8797                              * eval:var:dom
8798                              * eval:var:html
8799                              * 
8800                              */
8801                            window.eval(match[2]);
8802                         }
8803                     }
8804                 }
8805                 var el = document.getElementById(id);
8806                 if(el){el.parentNode.removeChild(el);}
8807                 if(typeof callback == "function"){
8808                     callback();
8809                 }
8810             });
8811             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8812             return this;
8813         },
8814
8815         /**
8816          * Direct access to the UpdateManager update() method (takes the same parameters).
8817          * @param {String/Function} url The url for this request or a function to call to get the url
8818          * @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}
8819          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8820          * @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.
8821          * @return {Roo.Element} this
8822          */
8823         load : function(){
8824             var um = this.getUpdateManager();
8825             um.update.apply(um, arguments);
8826             return this;
8827         },
8828
8829         /**
8830         * Gets this element's UpdateManager
8831         * @return {Roo.UpdateManager} The UpdateManager
8832         */
8833         getUpdateManager : function(){
8834             if(!this.updateManager){
8835                 this.updateManager = new Roo.UpdateManager(this);
8836             }
8837             return this.updateManager;
8838         },
8839
8840         /**
8841          * Disables text selection for this element (normalized across browsers)
8842          * @return {Roo.Element} this
8843          */
8844         unselectable : function(){
8845             this.dom.unselectable = "on";
8846             this.swallowEvent("selectstart", true);
8847             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8848             this.addClass("x-unselectable");
8849             return this;
8850         },
8851
8852         /**
8853         * Calculates the x, y to center this element on the screen
8854         * @return {Array} The x, y values [x, y]
8855         */
8856         getCenterXY : function(){
8857             return this.getAlignToXY(document, 'c-c');
8858         },
8859
8860         /**
8861         * Centers the Element in either the viewport, or another Element.
8862         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8863         */
8864         center : function(centerIn){
8865             this.alignTo(centerIn || document, 'c-c');
8866             return this;
8867         },
8868
8869         /**
8870          * Tests various css rules/browsers to determine if this element uses a border box
8871          * @return {Boolean}
8872          */
8873         isBorderBox : function(){
8874             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8875         },
8876
8877         /**
8878          * Return a box {x, y, width, height} that can be used to set another elements
8879          * size/location to match this element.
8880          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8881          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8882          * @return {Object} box An object in the format {x, y, width, height}
8883          */
8884         getBox : function(contentBox, local){
8885             var xy;
8886             if(!local){
8887                 xy = this.getXY();
8888             }else{
8889                 var left = parseInt(this.getStyle("left"), 10) || 0;
8890                 var top = parseInt(this.getStyle("top"), 10) || 0;
8891                 xy = [left, top];
8892             }
8893             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8894             if(!contentBox){
8895                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8896             }else{
8897                 var l = this.getBorderWidth("l")+this.getPadding("l");
8898                 var r = this.getBorderWidth("r")+this.getPadding("r");
8899                 var t = this.getBorderWidth("t")+this.getPadding("t");
8900                 var b = this.getBorderWidth("b")+this.getPadding("b");
8901                 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)};
8902             }
8903             bx.right = bx.x + bx.width;
8904             bx.bottom = bx.y + bx.height;
8905             return bx;
8906         },
8907
8908         /**
8909          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8910          for more information about the sides.
8911          * @param {String} sides
8912          * @return {Number}
8913          */
8914         getFrameWidth : function(sides, onlyContentBox){
8915             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8916         },
8917
8918         /**
8919          * 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.
8920          * @param {Object} box The box to fill {x, y, width, height}
8921          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8922          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8923          * @return {Roo.Element} this
8924          */
8925         setBox : function(box, adjust, animate){
8926             var w = box.width, h = box.height;
8927             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8928                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8929                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8930             }
8931             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8932             return this;
8933         },
8934
8935         /**
8936          * Forces the browser to repaint this element
8937          * @return {Roo.Element} this
8938          */
8939          repaint : function(){
8940             var dom = this.dom;
8941             this.addClass("x-repaint");
8942             setTimeout(function(){
8943                 Roo.get(dom).removeClass("x-repaint");
8944             }, 1);
8945             return this;
8946         },
8947
8948         /**
8949          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8950          * then it returns the calculated width of the sides (see getPadding)
8951          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8952          * @return {Object/Number}
8953          */
8954         getMargins : function(side){
8955             if(!side){
8956                 return {
8957                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8958                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8959                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8960                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8961                 };
8962             }else{
8963                 return this.addStyles(side, El.margins);
8964              }
8965         },
8966
8967         // private
8968         addStyles : function(sides, styles){
8969             var val = 0, v, w;
8970             for(var i = 0, len = sides.length; i < len; i++){
8971                 v = this.getStyle(styles[sides.charAt(i)]);
8972                 if(v){
8973                      w = parseInt(v, 10);
8974                      if(w){ val += w; }
8975                 }
8976             }
8977             return val;
8978         },
8979
8980         /**
8981          * Creates a proxy element of this element
8982          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8983          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8984          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8985          * @return {Roo.Element} The new proxy element
8986          */
8987         createProxy : function(config, renderTo, matchBox){
8988             if(renderTo){
8989                 renderTo = Roo.getDom(renderTo);
8990             }else{
8991                 renderTo = document.body;
8992             }
8993             config = typeof config == "object" ?
8994                 config : {tag : "div", cls: config};
8995             var proxy = Roo.DomHelper.append(renderTo, config, true);
8996             if(matchBox){
8997                proxy.setBox(this.getBox());
8998             }
8999             return proxy;
9000         },
9001
9002         /**
9003          * Puts a mask over this element to disable user interaction. Requires core.css.
9004          * This method can only be applied to elements which accept child nodes.
9005          * @param {String} msg (optional) A message to display in the mask
9006          * @param {String} msgCls (optional) A css class to apply to the msg element
9007          * @return {Element} The mask  element
9008          */
9009         mask : function(msg, msgCls)
9010         {
9011             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9012                 this.setStyle("position", "relative");
9013             }
9014             if(!this._mask){
9015                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9016             }
9017             this.addClass("x-masked");
9018             this._mask.setDisplayed(true);
9019             
9020             // we wander
9021             var z = 0;
9022             var dom = this.dom;
9023             while (dom && dom.style) {
9024                 if (!isNaN(parseInt(dom.style.zIndex))) {
9025                     z = Math.max(z, parseInt(dom.style.zIndex));
9026                 }
9027                 dom = dom.parentNode;
9028             }
9029             // if we are masking the body - then it hides everything..
9030             if (this.dom == document.body) {
9031                 z = 1000000;
9032                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9033                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9034             }
9035            
9036             if(typeof msg == 'string'){
9037                 if(!this._maskMsg){
9038                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9039                 }
9040                 var mm = this._maskMsg;
9041                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9042                 if (mm.dom.firstChild) { // weird IE issue?
9043                     mm.dom.firstChild.innerHTML = msg;
9044                 }
9045                 mm.setDisplayed(true);
9046                 mm.center(this);
9047                 mm.setStyle('z-index', z + 102);
9048             }
9049             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9050                 this._mask.setHeight(this.getHeight());
9051             }
9052             this._mask.setStyle('z-index', z + 100);
9053             
9054             return this._mask;
9055         },
9056
9057         /**
9058          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9059          * it is cached for reuse.
9060          */
9061         unmask : function(removeEl){
9062             if(this._mask){
9063                 if(removeEl === true){
9064                     this._mask.remove();
9065                     delete this._mask;
9066                     if(this._maskMsg){
9067                         this._maskMsg.remove();
9068                         delete this._maskMsg;
9069                     }
9070                 }else{
9071                     this._mask.setDisplayed(false);
9072                     if(this._maskMsg){
9073                         this._maskMsg.setDisplayed(false);
9074                     }
9075                 }
9076             }
9077             this.removeClass("x-masked");
9078         },
9079
9080         /**
9081          * Returns true if this element is masked
9082          * @return {Boolean}
9083          */
9084         isMasked : function(){
9085             return this._mask && this._mask.isVisible();
9086         },
9087
9088         /**
9089          * Creates an iframe shim for this element to keep selects and other windowed objects from
9090          * showing through.
9091          * @return {Roo.Element} The new shim element
9092          */
9093         createShim : function(){
9094             var el = document.createElement('iframe');
9095             el.frameBorder = 'no';
9096             el.className = 'roo-shim';
9097             if(Roo.isIE && Roo.isSecure){
9098                 el.src = Roo.SSL_SECURE_URL;
9099             }
9100             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9101             shim.autoBoxAdjust = false;
9102             return shim;
9103         },
9104
9105         /**
9106          * Removes this element from the DOM and deletes it from the cache
9107          */
9108         remove : function(){
9109             if(this.dom.parentNode){
9110                 this.dom.parentNode.removeChild(this.dom);
9111             }
9112             delete El.cache[this.dom.id];
9113         },
9114
9115         /**
9116          * Sets up event handlers to add and remove a css class when the mouse is over this element
9117          * @param {String} className
9118          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9119          * mouseout events for children elements
9120          * @return {Roo.Element} this
9121          */
9122         addClassOnOver : function(className, preventFlicker){
9123             this.on("mouseover", function(){
9124                 Roo.fly(this, '_internal').addClass(className);
9125             }, this.dom);
9126             var removeFn = function(e){
9127                 if(preventFlicker !== true || !e.within(this, true)){
9128                     Roo.fly(this, '_internal').removeClass(className);
9129                 }
9130             };
9131             this.on("mouseout", removeFn, this.dom);
9132             return this;
9133         },
9134
9135         /**
9136          * Sets up event handlers to add and remove a css class when this element has the focus
9137          * @param {String} className
9138          * @return {Roo.Element} this
9139          */
9140         addClassOnFocus : function(className){
9141             this.on("focus", function(){
9142                 Roo.fly(this, '_internal').addClass(className);
9143             }, this.dom);
9144             this.on("blur", function(){
9145                 Roo.fly(this, '_internal').removeClass(className);
9146             }, this.dom);
9147             return this;
9148         },
9149         /**
9150          * 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)
9151          * @param {String} className
9152          * @return {Roo.Element} this
9153          */
9154         addClassOnClick : function(className){
9155             var dom = this.dom;
9156             this.on("mousedown", function(){
9157                 Roo.fly(dom, '_internal').addClass(className);
9158                 var d = Roo.get(document);
9159                 var fn = function(){
9160                     Roo.fly(dom, '_internal').removeClass(className);
9161                     d.removeListener("mouseup", fn);
9162                 };
9163                 d.on("mouseup", fn);
9164             });
9165             return this;
9166         },
9167
9168         /**
9169          * Stops the specified event from bubbling and optionally prevents the default action
9170          * @param {String} eventName
9171          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9172          * @return {Roo.Element} this
9173          */
9174         swallowEvent : function(eventName, preventDefault){
9175             var fn = function(e){
9176                 e.stopPropagation();
9177                 if(preventDefault){
9178                     e.preventDefault();
9179                 }
9180             };
9181             if(eventName instanceof Array){
9182                 for(var i = 0, len = eventName.length; i < len; i++){
9183                      this.on(eventName[i], fn);
9184                 }
9185                 return this;
9186             }
9187             this.on(eventName, fn);
9188             return this;
9189         },
9190
9191         /**
9192          * @private
9193          */
9194       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9195
9196         /**
9197          * Sizes this element to its parent element's dimensions performing
9198          * neccessary box adjustments.
9199          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9200          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9201          * @return {Roo.Element} this
9202          */
9203         fitToParent : function(monitorResize, targetParent) {
9204           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9205           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9206           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9207             return;
9208           }
9209           var p = Roo.get(targetParent || this.dom.parentNode);
9210           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9211           if (monitorResize === true) {
9212             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9213             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9214           }
9215           return this;
9216         },
9217
9218         /**
9219          * Gets the next sibling, skipping text nodes
9220          * @return {HTMLElement} The next sibling or null
9221          */
9222         getNextSibling : function(){
9223             var n = this.dom.nextSibling;
9224             while(n && n.nodeType != 1){
9225                 n = n.nextSibling;
9226             }
9227             return n;
9228         },
9229
9230         /**
9231          * Gets the previous sibling, skipping text nodes
9232          * @return {HTMLElement} The previous sibling or null
9233          */
9234         getPrevSibling : function(){
9235             var n = this.dom.previousSibling;
9236             while(n && n.nodeType != 1){
9237                 n = n.previousSibling;
9238             }
9239             return n;
9240         },
9241
9242
9243         /**
9244          * Appends the passed element(s) to this element
9245          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9246          * @return {Roo.Element} this
9247          */
9248         appendChild: function(el){
9249             el = Roo.get(el);
9250             el.appendTo(this);
9251             return this;
9252         },
9253
9254         /**
9255          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9256          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9257          * automatically generated with the specified attributes.
9258          * @param {HTMLElement} insertBefore (optional) a child element of this element
9259          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9260          * @return {Roo.Element} The new child element
9261          */
9262         createChild: function(config, insertBefore, returnDom){
9263             config = config || {tag:'div'};
9264             if(insertBefore){
9265                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9266             }
9267             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9268         },
9269
9270         /**
9271          * Appends this element to the passed element
9272          * @param {String/HTMLElement/Element} el The new parent element
9273          * @return {Roo.Element} this
9274          */
9275         appendTo: function(el){
9276             el = Roo.getDom(el);
9277             el.appendChild(this.dom);
9278             return this;
9279         },
9280
9281         /**
9282          * Inserts this element before the passed element in the DOM
9283          * @param {String/HTMLElement/Element} el The element to insert before
9284          * @return {Roo.Element} this
9285          */
9286         insertBefore: function(el){
9287             el = Roo.getDom(el);
9288             el.parentNode.insertBefore(this.dom, el);
9289             return this;
9290         },
9291
9292         /**
9293          * Inserts this element after the passed element in the DOM
9294          * @param {String/HTMLElement/Element} el The element to insert after
9295          * @return {Roo.Element} this
9296          */
9297         insertAfter: function(el){
9298             el = Roo.getDom(el);
9299             el.parentNode.insertBefore(this.dom, el.nextSibling);
9300             return this;
9301         },
9302
9303         /**
9304          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9305          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9306          * @return {Roo.Element} The new child
9307          */
9308         insertFirst: function(el, returnDom){
9309             el = el || {};
9310             if(typeof el == 'object' && !el.nodeType){ // dh config
9311                 return this.createChild(el, this.dom.firstChild, returnDom);
9312             }else{
9313                 el = Roo.getDom(el);
9314                 this.dom.insertBefore(el, this.dom.firstChild);
9315                 return !returnDom ? Roo.get(el) : el;
9316             }
9317         },
9318
9319         /**
9320          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9321          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9322          * @param {String} where (optional) 'before' or 'after' defaults to before
9323          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9324          * @return {Roo.Element} the inserted Element
9325          */
9326         insertSibling: function(el, where, returnDom){
9327             where = where ? where.toLowerCase() : 'before';
9328             el = el || {};
9329             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9330
9331             if(typeof el == 'object' && !el.nodeType){ // dh config
9332                 if(where == 'after' && !this.dom.nextSibling){
9333                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9334                 }else{
9335                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9336                 }
9337
9338             }else{
9339                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9340                             where == 'before' ? this.dom : this.dom.nextSibling);
9341                 if(!returnDom){
9342                     rt = Roo.get(rt);
9343                 }
9344             }
9345             return rt;
9346         },
9347
9348         /**
9349          * Creates and wraps this element with another element
9350          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9351          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9352          * @return {HTMLElement/Element} The newly created wrapper element
9353          */
9354         wrap: function(config, returnDom){
9355             if(!config){
9356                 config = {tag: "div"};
9357             }
9358             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9359             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9360             return newEl;
9361         },
9362
9363         /**
9364          * Replaces the passed element with this element
9365          * @param {String/HTMLElement/Element} el The element to replace
9366          * @return {Roo.Element} this
9367          */
9368         replace: function(el){
9369             el = Roo.get(el);
9370             this.insertBefore(el);
9371             el.remove();
9372             return this;
9373         },
9374
9375         /**
9376          * Inserts an html fragment into this element
9377          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9378          * @param {String} html The HTML fragment
9379          * @param {Boolean} returnEl True to return an Roo.Element
9380          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9381          */
9382         insertHtml : function(where, html, returnEl){
9383             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9384             return returnEl ? Roo.get(el) : el;
9385         },
9386
9387         /**
9388          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9389          * @param {Object} o The object with the attributes
9390          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9391          * @return {Roo.Element} this
9392          */
9393         set : function(o, useSet){
9394             var el = this.dom;
9395             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9396             for(var attr in o){
9397                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9398                 if(attr=="cls"){
9399                     el.className = o["cls"];
9400                 }else{
9401                     if(useSet) {
9402                         el.setAttribute(attr, o[attr]);
9403                     } else {
9404                         el[attr] = o[attr];
9405                     }
9406                 }
9407             }
9408             if(o.style){
9409                 Roo.DomHelper.applyStyles(el, o.style);
9410             }
9411             return this;
9412         },
9413
9414         /**
9415          * Convenience method for constructing a KeyMap
9416          * @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:
9417          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9418          * @param {Function} fn The function to call
9419          * @param {Object} scope (optional) The scope of the function
9420          * @return {Roo.KeyMap} The KeyMap created
9421          */
9422         addKeyListener : function(key, fn, scope){
9423             var config;
9424             if(typeof key != "object" || key instanceof Array){
9425                 config = {
9426                     key: key,
9427                     fn: fn,
9428                     scope: scope
9429                 };
9430             }else{
9431                 config = {
9432                     key : key.key,
9433                     shift : key.shift,
9434                     ctrl : key.ctrl,
9435                     alt : key.alt,
9436                     fn: fn,
9437                     scope: scope
9438                 };
9439             }
9440             return new Roo.KeyMap(this, config);
9441         },
9442
9443         /**
9444          * Creates a KeyMap for this element
9445          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9446          * @return {Roo.KeyMap} The KeyMap created
9447          */
9448         addKeyMap : function(config){
9449             return new Roo.KeyMap(this, config);
9450         },
9451
9452         /**
9453          * Returns true if this element is scrollable.
9454          * @return {Boolean}
9455          */
9456          isScrollable : function(){
9457             var dom = this.dom;
9458             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9459         },
9460
9461         /**
9462          * 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().
9463          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9464          * @param {Number} value The new scroll value
9465          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9466          * @return {Element} this
9467          */
9468
9469         scrollTo : function(side, value, animate){
9470             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9471             if(!animate || !A){
9472                 this.dom[prop] = value;
9473             }else{
9474                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9475                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9476             }
9477             return this;
9478         },
9479
9480         /**
9481          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9482          * within this element's scrollable range.
9483          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9484          * @param {Number} distance How far to scroll the element in pixels
9485          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9486          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9487          * was scrolled as far as it could go.
9488          */
9489          scroll : function(direction, distance, animate){
9490              if(!this.isScrollable()){
9491                  return;
9492              }
9493              var el = this.dom;
9494              var l = el.scrollLeft, t = el.scrollTop;
9495              var w = el.scrollWidth, h = el.scrollHeight;
9496              var cw = el.clientWidth, ch = el.clientHeight;
9497              direction = direction.toLowerCase();
9498              var scrolled = false;
9499              var a = this.preanim(arguments, 2);
9500              switch(direction){
9501                  case "l":
9502                  case "left":
9503                      if(w - l > cw){
9504                          var v = Math.min(l + distance, w-cw);
9505                          this.scrollTo("left", v, a);
9506                          scrolled = true;
9507                      }
9508                      break;
9509                 case "r":
9510                 case "right":
9511                      if(l > 0){
9512                          var v = Math.max(l - distance, 0);
9513                          this.scrollTo("left", v, a);
9514                          scrolled = true;
9515                      }
9516                      break;
9517                 case "t":
9518                 case "top":
9519                 case "up":
9520                      if(t > 0){
9521                          var v = Math.max(t - distance, 0);
9522                          this.scrollTo("top", v, a);
9523                          scrolled = true;
9524                      }
9525                      break;
9526                 case "b":
9527                 case "bottom":
9528                 case "down":
9529                      if(h - t > ch){
9530                          var v = Math.min(t + distance, h-ch);
9531                          this.scrollTo("top", v, a);
9532                          scrolled = true;
9533                      }
9534                      break;
9535              }
9536              return scrolled;
9537         },
9538
9539         /**
9540          * Translates the passed page coordinates into left/top css values for this element
9541          * @param {Number/Array} x The page x or an array containing [x, y]
9542          * @param {Number} y The page y
9543          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9544          */
9545         translatePoints : function(x, y){
9546             if(typeof x == 'object' || x instanceof Array){
9547                 y = x[1]; x = x[0];
9548             }
9549             var p = this.getStyle('position');
9550             var o = this.getXY();
9551
9552             var l = parseInt(this.getStyle('left'), 10);
9553             var t = parseInt(this.getStyle('top'), 10);
9554
9555             if(isNaN(l)){
9556                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9557             }
9558             if(isNaN(t)){
9559                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9560             }
9561
9562             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9563         },
9564
9565         /**
9566          * Returns the current scroll position of the element.
9567          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9568          */
9569         getScroll : function(){
9570             var d = this.dom, doc = document;
9571             if(d == doc || d == doc.body){
9572                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9573                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9574                 return {left: l, top: t};
9575             }else{
9576                 return {left: d.scrollLeft, top: d.scrollTop};
9577             }
9578         },
9579
9580         /**
9581          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9582          * are convert to standard 6 digit hex color.
9583          * @param {String} attr The css attribute
9584          * @param {String} defaultValue The default value to use when a valid color isn't found
9585          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9586          * YUI color anims.
9587          */
9588         getColor : function(attr, defaultValue, prefix){
9589             var v = this.getStyle(attr);
9590             if(!v || v == "transparent" || v == "inherit") {
9591                 return defaultValue;
9592             }
9593             var color = typeof prefix == "undefined" ? "#" : prefix;
9594             if(v.substr(0, 4) == "rgb("){
9595                 var rvs = v.slice(4, v.length -1).split(",");
9596                 for(var i = 0; i < 3; i++){
9597                     var h = parseInt(rvs[i]).toString(16);
9598                     if(h < 16){
9599                         h = "0" + h;
9600                     }
9601                     color += h;
9602                 }
9603             } else {
9604                 if(v.substr(0, 1) == "#"){
9605                     if(v.length == 4) {
9606                         for(var i = 1; i < 4; i++){
9607                             var c = v.charAt(i);
9608                             color +=  c + c;
9609                         }
9610                     }else if(v.length == 7){
9611                         color += v.substr(1);
9612                     }
9613                 }
9614             }
9615             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9616         },
9617
9618         /**
9619          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9620          * gradient background, rounded corners and a 4-way shadow.
9621          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9622          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9623          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9624          * @return {Roo.Element} this
9625          */
9626         boxWrap : function(cls){
9627             cls = cls || 'x-box';
9628             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9629             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9630             return el;
9631         },
9632
9633         /**
9634          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9635          * @param {String} namespace The namespace in which to look for the attribute
9636          * @param {String} name The attribute name
9637          * @return {String} The attribute value
9638          */
9639         getAttributeNS : Roo.isIE ? function(ns, name){
9640             var d = this.dom;
9641             var type = typeof d[ns+":"+name];
9642             if(type != 'undefined' && type != 'unknown'){
9643                 return d[ns+":"+name];
9644             }
9645             return d[name];
9646         } : function(ns, name){
9647             var d = this.dom;
9648             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9649         },
9650         
9651         
9652         /**
9653          * Sets or Returns the value the dom attribute value
9654          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9655          * @param {String} value (optional) The value to set the attribute to
9656          * @return {String} The attribute value
9657          */
9658         attr : function(name){
9659             if (arguments.length > 1) {
9660                 this.dom.setAttribute(name, arguments[1]);
9661                 return arguments[1];
9662             }
9663             if (typeof(name) == 'object') {
9664                 for(var i in name) {
9665                     this.attr(i, name[i]);
9666                 }
9667                 return name;
9668             }
9669             
9670             
9671             if (!this.dom.hasAttribute(name)) {
9672                 return undefined;
9673             }
9674             return this.dom.getAttribute(name);
9675         }
9676         
9677         
9678         
9679     };
9680
9681     var ep = El.prototype;
9682
9683     /**
9684      * Appends an event handler (Shorthand for addListener)
9685      * @param {String}   eventName     The type of event to append
9686      * @param {Function} fn        The method the event invokes
9687      * @param {Object} scope       (optional) The scope (this object) of the fn
9688      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9689      * @method
9690      */
9691     ep.on = ep.addListener;
9692         // backwards compat
9693     ep.mon = ep.addListener;
9694
9695     /**
9696      * Removes an event handler from this element (shorthand for removeListener)
9697      * @param {String} eventName the type of event to remove
9698      * @param {Function} fn the method the event invokes
9699      * @return {Roo.Element} this
9700      * @method
9701      */
9702     ep.un = ep.removeListener;
9703
9704     /**
9705      * true to automatically adjust width and height settings for box-model issues (default to true)
9706      */
9707     ep.autoBoxAdjust = true;
9708
9709     // private
9710     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9711
9712     // private
9713     El.addUnits = function(v, defaultUnit){
9714         if(v === "" || v == "auto"){
9715             return v;
9716         }
9717         if(v === undefined){
9718             return '';
9719         }
9720         if(typeof v == "number" || !El.unitPattern.test(v)){
9721             return v + (defaultUnit || 'px');
9722         }
9723         return v;
9724     };
9725
9726     // special markup used throughout Roo when box wrapping elements
9727     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>';
9728     /**
9729      * Visibility mode constant - Use visibility to hide element
9730      * @static
9731      * @type Number
9732      */
9733     El.VISIBILITY = 1;
9734     /**
9735      * Visibility mode constant - Use display to hide element
9736      * @static
9737      * @type Number
9738      */
9739     El.DISPLAY = 2;
9740
9741     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9742     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9743     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9744
9745
9746
9747     /**
9748      * @private
9749      */
9750     El.cache = {};
9751
9752     var docEl;
9753
9754     /**
9755      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9756      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9757      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9758      * @return {Element} The Element object
9759      * @static
9760      */
9761     El.get = function(el){
9762         var ex, elm, id;
9763         if(!el){ return null; }
9764         if(typeof el == "string"){ // element id
9765             if(!(elm = document.getElementById(el))){
9766                 return null;
9767             }
9768             if(ex = El.cache[el]){
9769                 ex.dom = elm;
9770             }else{
9771                 ex = El.cache[el] = new El(elm);
9772             }
9773             return ex;
9774         }else if(el.tagName){ // dom element
9775             if(!(id = el.id)){
9776                 id = Roo.id(el);
9777             }
9778             if(ex = El.cache[id]){
9779                 ex.dom = el;
9780             }else{
9781                 ex = El.cache[id] = new El(el);
9782             }
9783             return ex;
9784         }else if(el instanceof El){
9785             if(el != docEl){
9786                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9787                                                               // catch case where it hasn't been appended
9788                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9789             }
9790             return el;
9791         }else if(el.isComposite){
9792             return el;
9793         }else if(el instanceof Array){
9794             return El.select(el);
9795         }else if(el == document){
9796             // create a bogus element object representing the document object
9797             if(!docEl){
9798                 var f = function(){};
9799                 f.prototype = El.prototype;
9800                 docEl = new f();
9801                 docEl.dom = document;
9802             }
9803             return docEl;
9804         }
9805         return null;
9806     };
9807
9808     // private
9809     El.uncache = function(el){
9810         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9811             if(a[i]){
9812                 delete El.cache[a[i].id || a[i]];
9813             }
9814         }
9815     };
9816
9817     // private
9818     // Garbage collection - uncache elements/purge listeners on orphaned elements
9819     // so we don't hold a reference and cause the browser to retain them
9820     El.garbageCollect = function(){
9821         if(!Roo.enableGarbageCollector){
9822             clearInterval(El.collectorThread);
9823             return;
9824         }
9825         for(var eid in El.cache){
9826             var el = El.cache[eid], d = el.dom;
9827             // -------------------------------------------------------
9828             // Determining what is garbage:
9829             // -------------------------------------------------------
9830             // !d
9831             // dom node is null, definitely garbage
9832             // -------------------------------------------------------
9833             // !d.parentNode
9834             // no parentNode == direct orphan, definitely garbage
9835             // -------------------------------------------------------
9836             // !d.offsetParent && !document.getElementById(eid)
9837             // display none elements have no offsetParent so we will
9838             // also try to look it up by it's id. However, check
9839             // offsetParent first so we don't do unneeded lookups.
9840             // This enables collection of elements that are not orphans
9841             // directly, but somewhere up the line they have an orphan
9842             // parent.
9843             // -------------------------------------------------------
9844             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9845                 delete El.cache[eid];
9846                 if(d && Roo.enableListenerCollection){
9847                     E.purgeElement(d);
9848                 }
9849             }
9850         }
9851     }
9852     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9853
9854
9855     // dom is optional
9856     El.Flyweight = function(dom){
9857         this.dom = dom;
9858     };
9859     El.Flyweight.prototype = El.prototype;
9860
9861     El._flyweights = {};
9862     /**
9863      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9864      * the dom node can be overwritten by other code.
9865      * @param {String/HTMLElement} el The dom node or id
9866      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9867      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9868      * @static
9869      * @return {Element} The shared Element object
9870      */
9871     El.fly = function(el, named){
9872         named = named || '_global';
9873         el = Roo.getDom(el);
9874         if(!el){
9875             return null;
9876         }
9877         if(!El._flyweights[named]){
9878             El._flyweights[named] = new El.Flyweight();
9879         }
9880         El._flyweights[named].dom = el;
9881         return El._flyweights[named];
9882     };
9883
9884     /**
9885      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9886      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9887      * Shorthand of {@link Roo.Element#get}
9888      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9889      * @return {Element} The Element object
9890      * @member Roo
9891      * @method get
9892      */
9893     Roo.get = El.get;
9894     /**
9895      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9896      * the dom node can be overwritten by other code.
9897      * Shorthand of {@link Roo.Element#fly}
9898      * @param {String/HTMLElement} el The dom node or id
9899      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9900      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9901      * @static
9902      * @return {Element} The shared Element object
9903      * @member Roo
9904      * @method fly
9905      */
9906     Roo.fly = El.fly;
9907
9908     // speedy lookup for elements never to box adjust
9909     var noBoxAdjust = Roo.isStrict ? {
9910         select:1
9911     } : {
9912         input:1, select:1, textarea:1
9913     };
9914     if(Roo.isIE || Roo.isGecko){
9915         noBoxAdjust['button'] = 1;
9916     }
9917
9918
9919     Roo.EventManager.on(window, 'unload', function(){
9920         delete El.cache;
9921         delete El._flyweights;
9922     });
9923 })();
9924
9925
9926
9927
9928 if(Roo.DomQuery){
9929     Roo.Element.selectorFunction = Roo.DomQuery.select;
9930 }
9931
9932 Roo.Element.select = function(selector, unique, root){
9933     var els;
9934     if(typeof selector == "string"){
9935         els = Roo.Element.selectorFunction(selector, root);
9936     }else if(selector.length !== undefined){
9937         els = selector;
9938     }else{
9939         throw "Invalid selector";
9940     }
9941     if(unique === true){
9942         return new Roo.CompositeElement(els);
9943     }else{
9944         return new Roo.CompositeElementLite(els);
9945     }
9946 };
9947 /**
9948  * Selects elements based on the passed CSS selector to enable working on them as 1.
9949  * @param {String/Array} selector The CSS selector or an array of elements
9950  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9951  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9952  * @return {CompositeElementLite/CompositeElement}
9953  * @member Roo
9954  * @method select
9955  */
9956 Roo.select = Roo.Element.select;
9957
9958
9959
9960
9961
9962
9963
9964
9965
9966
9967
9968
9969
9970
9971 /*
9972  * Based on:
9973  * Ext JS Library 1.1.1
9974  * Copyright(c) 2006-2007, Ext JS, LLC.
9975  *
9976  * Originally Released Under LGPL - original licence link has changed is not relivant.
9977  *
9978  * Fork - LGPL
9979  * <script type="text/javascript">
9980  */
9981
9982
9983
9984 //Notifies Element that fx methods are available
9985 Roo.enableFx = true;
9986
9987 /**
9988  * @class Roo.Fx
9989  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9990  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9991  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9992  * Element effects to work.</p><br/>
9993  *
9994  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9995  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9996  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9997  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9998  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9999  * expected results and should be done with care.</p><br/>
10000  *
10001  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10002  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10003 <pre>
10004 Value  Description
10005 -----  -----------------------------
10006 tl     The top left corner
10007 t      The center of the top edge
10008 tr     The top right corner
10009 l      The center of the left edge
10010 r      The center of the right edge
10011 bl     The bottom left corner
10012 b      The center of the bottom edge
10013 br     The bottom right corner
10014 </pre>
10015  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10016  * below are common options that can be passed to any Fx method.</b>
10017  * @cfg {Function} callback A function called when the effect is finished
10018  * @cfg {Object} scope The scope of the effect function
10019  * @cfg {String} easing A valid Easing value for the effect
10020  * @cfg {String} afterCls A css class to apply after the effect
10021  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10022  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10023  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10024  * effects that end with the element being visually hidden, ignored otherwise)
10025  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10026  * a function which returns such a specification that will be applied to the Element after the effect finishes
10027  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10028  * @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
10029  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10030  */
10031 Roo.Fx = {
10032         /**
10033          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10034          * origin for the slide effect.  This function automatically handles wrapping the element with
10035          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10036          * Usage:
10037          *<pre><code>
10038 // default: slide the element in from the top
10039 el.slideIn();
10040
10041 // custom: slide the element in from the right with a 2-second duration
10042 el.slideIn('r', { duration: 2 });
10043
10044 // common config options shown with default values
10045 el.slideIn('t', {
10046     easing: 'easeOut',
10047     duration: .5
10048 });
10049 </code></pre>
10050          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10051          * @param {Object} options (optional) Object literal with any of the Fx config options
10052          * @return {Roo.Element} The Element
10053          */
10054     slideIn : function(anchor, o){
10055         var el = this.getFxEl();
10056         o = o || {};
10057
10058         el.queueFx(o, function(){
10059
10060             anchor = anchor || "t";
10061
10062             // fix display to visibility
10063             this.fixDisplay();
10064
10065             // restore values after effect
10066             var r = this.getFxRestore();
10067             var b = this.getBox();
10068             // fixed size for slide
10069             this.setSize(b);
10070
10071             // wrap if needed
10072             var wrap = this.fxWrap(r.pos, o, "hidden");
10073
10074             var st = this.dom.style;
10075             st.visibility = "visible";
10076             st.position = "absolute";
10077
10078             // clear out temp styles after slide and unwrap
10079             var after = function(){
10080                 el.fxUnwrap(wrap, r.pos, o);
10081                 st.width = r.width;
10082                 st.height = r.height;
10083                 el.afterFx(o);
10084             };
10085             // time to calc the positions
10086             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10087
10088             switch(anchor.toLowerCase()){
10089                 case "t":
10090                     wrap.setSize(b.width, 0);
10091                     st.left = st.bottom = "0";
10092                     a = {height: bh};
10093                 break;
10094                 case "l":
10095                     wrap.setSize(0, b.height);
10096                     st.right = st.top = "0";
10097                     a = {width: bw};
10098                 break;
10099                 case "r":
10100                     wrap.setSize(0, b.height);
10101                     wrap.setX(b.right);
10102                     st.left = st.top = "0";
10103                     a = {width: bw, points: pt};
10104                 break;
10105                 case "b":
10106                     wrap.setSize(b.width, 0);
10107                     wrap.setY(b.bottom);
10108                     st.left = st.top = "0";
10109                     a = {height: bh, points: pt};
10110                 break;
10111                 case "tl":
10112                     wrap.setSize(0, 0);
10113                     st.right = st.bottom = "0";
10114                     a = {width: bw, height: bh};
10115                 break;
10116                 case "bl":
10117                     wrap.setSize(0, 0);
10118                     wrap.setY(b.y+b.height);
10119                     st.right = st.top = "0";
10120                     a = {width: bw, height: bh, points: pt};
10121                 break;
10122                 case "br":
10123                     wrap.setSize(0, 0);
10124                     wrap.setXY([b.right, b.bottom]);
10125                     st.left = st.top = "0";
10126                     a = {width: bw, height: bh, points: pt};
10127                 break;
10128                 case "tr":
10129                     wrap.setSize(0, 0);
10130                     wrap.setX(b.x+b.width);
10131                     st.left = st.bottom = "0";
10132                     a = {width: bw, height: bh, points: pt};
10133                 break;
10134             }
10135             this.dom.style.visibility = "visible";
10136             wrap.show();
10137
10138             arguments.callee.anim = wrap.fxanim(a,
10139                 o,
10140                 'motion',
10141                 .5,
10142                 'easeOut', after);
10143         });
10144         return this;
10145     },
10146     
10147         /**
10148          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10149          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10150          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10151          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10152          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10153          * Usage:
10154          *<pre><code>
10155 // default: slide the element out to the top
10156 el.slideOut();
10157
10158 // custom: slide the element out to the right with a 2-second duration
10159 el.slideOut('r', { duration: 2 });
10160
10161 // common config options shown with default values
10162 el.slideOut('t', {
10163     easing: 'easeOut',
10164     duration: .5,
10165     remove: false,
10166     useDisplay: false
10167 });
10168 </code></pre>
10169          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10170          * @param {Object} options (optional) Object literal with any of the Fx config options
10171          * @return {Roo.Element} The Element
10172          */
10173     slideOut : function(anchor, o){
10174         var el = this.getFxEl();
10175         o = o || {};
10176
10177         el.queueFx(o, function(){
10178
10179             anchor = anchor || "t";
10180
10181             // restore values after effect
10182             var r = this.getFxRestore();
10183             
10184             var b = this.getBox();
10185             // fixed size for slide
10186             this.setSize(b);
10187
10188             // wrap if needed
10189             var wrap = this.fxWrap(r.pos, o, "visible");
10190
10191             var st = this.dom.style;
10192             st.visibility = "visible";
10193             st.position = "absolute";
10194
10195             wrap.setSize(b);
10196
10197             var after = function(){
10198                 if(o.useDisplay){
10199                     el.setDisplayed(false);
10200                 }else{
10201                     el.hide();
10202                 }
10203
10204                 el.fxUnwrap(wrap, r.pos, o);
10205
10206                 st.width = r.width;
10207                 st.height = r.height;
10208
10209                 el.afterFx(o);
10210             };
10211
10212             var a, zero = {to: 0};
10213             switch(anchor.toLowerCase()){
10214                 case "t":
10215                     st.left = st.bottom = "0";
10216                     a = {height: zero};
10217                 break;
10218                 case "l":
10219                     st.right = st.top = "0";
10220                     a = {width: zero};
10221                 break;
10222                 case "r":
10223                     st.left = st.top = "0";
10224                     a = {width: zero, points: {to:[b.right, b.y]}};
10225                 break;
10226                 case "b":
10227                     st.left = st.top = "0";
10228                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10229                 break;
10230                 case "tl":
10231                     st.right = st.bottom = "0";
10232                     a = {width: zero, height: zero};
10233                 break;
10234                 case "bl":
10235                     st.right = st.top = "0";
10236                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10237                 break;
10238                 case "br":
10239                     st.left = st.top = "0";
10240                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10241                 break;
10242                 case "tr":
10243                     st.left = st.bottom = "0";
10244                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10245                 break;
10246             }
10247
10248             arguments.callee.anim = wrap.fxanim(a,
10249                 o,
10250                 'motion',
10251                 .5,
10252                 "easeOut", after);
10253         });
10254         return this;
10255     },
10256
10257         /**
10258          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10259          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10260          * The element must be removed from the DOM using the 'remove' config option if desired.
10261          * Usage:
10262          *<pre><code>
10263 // default
10264 el.puff();
10265
10266 // common config options shown with default values
10267 el.puff({
10268     easing: 'easeOut',
10269     duration: .5,
10270     remove: false,
10271     useDisplay: false
10272 });
10273 </code></pre>
10274          * @param {Object} options (optional) Object literal with any of the Fx config options
10275          * @return {Roo.Element} The Element
10276          */
10277     puff : function(o){
10278         var el = this.getFxEl();
10279         o = o || {};
10280
10281         el.queueFx(o, function(){
10282             this.clearOpacity();
10283             this.show();
10284
10285             // restore values after effect
10286             var r = this.getFxRestore();
10287             var st = this.dom.style;
10288
10289             var after = function(){
10290                 if(o.useDisplay){
10291                     el.setDisplayed(false);
10292                 }else{
10293                     el.hide();
10294                 }
10295
10296                 el.clearOpacity();
10297
10298                 el.setPositioning(r.pos);
10299                 st.width = r.width;
10300                 st.height = r.height;
10301                 st.fontSize = '';
10302                 el.afterFx(o);
10303             };
10304
10305             var width = this.getWidth();
10306             var height = this.getHeight();
10307
10308             arguments.callee.anim = this.fxanim({
10309                     width : {to: this.adjustWidth(width * 2)},
10310                     height : {to: this.adjustHeight(height * 2)},
10311                     points : {by: [-(width * .5), -(height * .5)]},
10312                     opacity : {to: 0},
10313                     fontSize: {to:200, unit: "%"}
10314                 },
10315                 o,
10316                 'motion',
10317                 .5,
10318                 "easeOut", after);
10319         });
10320         return this;
10321     },
10322
10323         /**
10324          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10325          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10326          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10327          * Usage:
10328          *<pre><code>
10329 // default
10330 el.switchOff();
10331
10332 // all config options shown with default values
10333 el.switchOff({
10334     easing: 'easeIn',
10335     duration: .3,
10336     remove: false,
10337     useDisplay: false
10338 });
10339 </code></pre>
10340          * @param {Object} options (optional) Object literal with any of the Fx config options
10341          * @return {Roo.Element} The Element
10342          */
10343     switchOff : function(o){
10344         var el = this.getFxEl();
10345         o = o || {};
10346
10347         el.queueFx(o, function(){
10348             this.clearOpacity();
10349             this.clip();
10350
10351             // restore values after effect
10352             var r = this.getFxRestore();
10353             var st = this.dom.style;
10354
10355             var after = function(){
10356                 if(o.useDisplay){
10357                     el.setDisplayed(false);
10358                 }else{
10359                     el.hide();
10360                 }
10361
10362                 el.clearOpacity();
10363                 el.setPositioning(r.pos);
10364                 st.width = r.width;
10365                 st.height = r.height;
10366
10367                 el.afterFx(o);
10368             };
10369
10370             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10371                 this.clearOpacity();
10372                 (function(){
10373                     this.fxanim({
10374                         height:{to:1},
10375                         points:{by:[0, this.getHeight() * .5]}
10376                     }, o, 'motion', 0.3, 'easeIn', after);
10377                 }).defer(100, this);
10378             });
10379         });
10380         return this;
10381     },
10382
10383     /**
10384      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10385      * changed using the "attr" config option) and then fading back to the original color. If no original
10386      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10387      * Usage:
10388 <pre><code>
10389 // default: highlight background to yellow
10390 el.highlight();
10391
10392 // custom: highlight foreground text to blue for 2 seconds
10393 el.highlight("0000ff", { attr: 'color', duration: 2 });
10394
10395 // common config options shown with default values
10396 el.highlight("ffff9c", {
10397     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10398     endColor: (current color) or "ffffff",
10399     easing: 'easeIn',
10400     duration: 1
10401 });
10402 </code></pre>
10403      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10404      * @param {Object} options (optional) Object literal with any of the Fx config options
10405      * @return {Roo.Element} The Element
10406      */ 
10407     highlight : function(color, o){
10408         var el = this.getFxEl();
10409         o = o || {};
10410
10411         el.queueFx(o, function(){
10412             color = color || "ffff9c";
10413             attr = o.attr || "backgroundColor";
10414
10415             this.clearOpacity();
10416             this.show();
10417
10418             var origColor = this.getColor(attr);
10419             var restoreColor = this.dom.style[attr];
10420             endColor = (o.endColor || origColor) || "ffffff";
10421
10422             var after = function(){
10423                 el.dom.style[attr] = restoreColor;
10424                 el.afterFx(o);
10425             };
10426
10427             var a = {};
10428             a[attr] = {from: color, to: endColor};
10429             arguments.callee.anim = this.fxanim(a,
10430                 o,
10431                 'color',
10432                 1,
10433                 'easeIn', after);
10434         });
10435         return this;
10436     },
10437
10438    /**
10439     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10440     * Usage:
10441 <pre><code>
10442 // default: a single light blue ripple
10443 el.frame();
10444
10445 // custom: 3 red ripples lasting 3 seconds total
10446 el.frame("ff0000", 3, { duration: 3 });
10447
10448 // common config options shown with default values
10449 el.frame("C3DAF9", 1, {
10450     duration: 1 //duration of entire animation (not each individual ripple)
10451     // Note: Easing is not configurable and will be ignored if included
10452 });
10453 </code></pre>
10454     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10455     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10456     * @param {Object} options (optional) Object literal with any of the Fx config options
10457     * @return {Roo.Element} The Element
10458     */
10459     frame : function(color, count, o){
10460         var el = this.getFxEl();
10461         o = o || {};
10462
10463         el.queueFx(o, function(){
10464             color = color || "#C3DAF9";
10465             if(color.length == 6){
10466                 color = "#" + color;
10467             }
10468             count = count || 1;
10469             duration = o.duration || 1;
10470             this.show();
10471
10472             var b = this.getBox();
10473             var animFn = function(){
10474                 var proxy = this.createProxy({
10475
10476                      style:{
10477                         visbility:"hidden",
10478                         position:"absolute",
10479                         "z-index":"35000", // yee haw
10480                         border:"0px solid " + color
10481                      }
10482                   });
10483                 var scale = Roo.isBorderBox ? 2 : 1;
10484                 proxy.animate({
10485                     top:{from:b.y, to:b.y - 20},
10486                     left:{from:b.x, to:b.x - 20},
10487                     borderWidth:{from:0, to:10},
10488                     opacity:{from:1, to:0},
10489                     height:{from:b.height, to:(b.height + (20*scale))},
10490                     width:{from:b.width, to:(b.width + (20*scale))}
10491                 }, duration, function(){
10492                     proxy.remove();
10493                 });
10494                 if(--count > 0){
10495                      animFn.defer((duration/2)*1000, this);
10496                 }else{
10497                     el.afterFx(o);
10498                 }
10499             };
10500             animFn.call(this);
10501         });
10502         return this;
10503     },
10504
10505    /**
10506     * Creates a pause before any subsequent queued effects begin.  If there are
10507     * no effects queued after the pause it will have no effect.
10508     * Usage:
10509 <pre><code>
10510 el.pause(1);
10511 </code></pre>
10512     * @param {Number} seconds The length of time to pause (in seconds)
10513     * @return {Roo.Element} The Element
10514     */
10515     pause : function(seconds){
10516         var el = this.getFxEl();
10517         var o = {};
10518
10519         el.queueFx(o, function(){
10520             setTimeout(function(){
10521                 el.afterFx(o);
10522             }, seconds * 1000);
10523         });
10524         return this;
10525     },
10526
10527    /**
10528     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10529     * using the "endOpacity" config option.
10530     * Usage:
10531 <pre><code>
10532 // default: fade in from opacity 0 to 100%
10533 el.fadeIn();
10534
10535 // custom: fade in from opacity 0 to 75% over 2 seconds
10536 el.fadeIn({ endOpacity: .75, duration: 2});
10537
10538 // common config options shown with default values
10539 el.fadeIn({
10540     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10541     easing: 'easeOut',
10542     duration: .5
10543 });
10544 </code></pre>
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     fadeIn : function(o){
10549         var el = this.getFxEl();
10550         o = o || {};
10551         el.queueFx(o, function(){
10552             this.setOpacity(0);
10553             this.fixDisplay();
10554             this.dom.style.visibility = 'visible';
10555             var to = o.endOpacity || 1;
10556             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10557                 o, null, .5, "easeOut", function(){
10558                 if(to == 1){
10559                     this.clearOpacity();
10560                 }
10561                 el.afterFx(o);
10562             });
10563         });
10564         return this;
10565     },
10566
10567    /**
10568     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10569     * using the "endOpacity" config option.
10570     * Usage:
10571 <pre><code>
10572 // default: fade out from the element's current opacity to 0
10573 el.fadeOut();
10574
10575 // custom: fade out from the element's current opacity to 25% over 2 seconds
10576 el.fadeOut({ endOpacity: .25, duration: 2});
10577
10578 // common config options shown with default values
10579 el.fadeOut({
10580     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10581     easing: 'easeOut',
10582     duration: .5
10583     remove: false,
10584     useDisplay: false
10585 });
10586 </code></pre>
10587     * @param {Object} options (optional) Object literal with any of the Fx config options
10588     * @return {Roo.Element} The Element
10589     */
10590     fadeOut : function(o){
10591         var el = this.getFxEl();
10592         o = o || {};
10593         el.queueFx(o, function(){
10594             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10595                 o, null, .5, "easeOut", function(){
10596                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10597                      this.dom.style.display = "none";
10598                 }else{
10599                      this.dom.style.visibility = "hidden";
10600                 }
10601                 this.clearOpacity();
10602                 el.afterFx(o);
10603             });
10604         });
10605         return this;
10606     },
10607
10608    /**
10609     * Animates the transition of an element's dimensions from a starting height/width
10610     * to an ending height/width.
10611     * Usage:
10612 <pre><code>
10613 // change height and width to 100x100 pixels
10614 el.scale(100, 100);
10615
10616 // common config options shown with default values.  The height and width will default to
10617 // the element's existing values if passed as null.
10618 el.scale(
10619     [element's width],
10620     [element's height], {
10621     easing: 'easeOut',
10622     duration: .35
10623 });
10624 </code></pre>
10625     * @param {Number} width  The new width (pass undefined to keep the original width)
10626     * @param {Number} height  The new height (pass undefined to keep the original height)
10627     * @param {Object} options (optional) Object literal with any of the Fx config options
10628     * @return {Roo.Element} The Element
10629     */
10630     scale : function(w, h, o){
10631         this.shift(Roo.apply({}, o, {
10632             width: w,
10633             height: h
10634         }));
10635         return this;
10636     },
10637
10638    /**
10639     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10640     * Any of these properties not specified in the config object will not be changed.  This effect 
10641     * requires that at least one new dimension, position or opacity setting must be passed in on
10642     * the config object in order for the function to have any effect.
10643     * Usage:
10644 <pre><code>
10645 // slide the element horizontally to x position 200 while changing the height and opacity
10646 el.shift({ x: 200, height: 50, opacity: .8 });
10647
10648 // common config options shown with default values.
10649 el.shift({
10650     width: [element's width],
10651     height: [element's height],
10652     x: [element's x position],
10653     y: [element's y position],
10654     opacity: [element's opacity],
10655     easing: 'easeOut',
10656     duration: .35
10657 });
10658 </code></pre>
10659     * @param {Object} options  Object literal with any of the Fx config options
10660     * @return {Roo.Element} The Element
10661     */
10662     shift : function(o){
10663         var el = this.getFxEl();
10664         o = o || {};
10665         el.queueFx(o, function(){
10666             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10667             if(w !== undefined){
10668                 a.width = {to: this.adjustWidth(w)};
10669             }
10670             if(h !== undefined){
10671                 a.height = {to: this.adjustHeight(h)};
10672             }
10673             if(x !== undefined || y !== undefined){
10674                 a.points = {to: [
10675                     x !== undefined ? x : this.getX(),
10676                     y !== undefined ? y : this.getY()
10677                 ]};
10678             }
10679             if(op !== undefined){
10680                 a.opacity = {to: op};
10681             }
10682             if(o.xy !== undefined){
10683                 a.points = {to: o.xy};
10684             }
10685             arguments.callee.anim = this.fxanim(a,
10686                 o, 'motion', .35, "easeOut", function(){
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693         /**
10694          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10695          * ending point of the effect.
10696          * Usage:
10697          *<pre><code>
10698 // default: slide the element downward while fading out
10699 el.ghost();
10700
10701 // custom: slide the element out to the right with a 2-second duration
10702 el.ghost('r', { duration: 2 });
10703
10704 // common config options shown with default values
10705 el.ghost('b', {
10706     easing: 'easeOut',
10707     duration: .5
10708     remove: false,
10709     useDisplay: false
10710 });
10711 </code></pre>
10712          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10713          * @param {Object} options (optional) Object literal with any of the Fx config options
10714          * @return {Roo.Element} The Element
10715          */
10716     ghost : function(anchor, o){
10717         var el = this.getFxEl();
10718         o = o || {};
10719
10720         el.queueFx(o, function(){
10721             anchor = anchor || "b";
10722
10723             // restore values after effect
10724             var r = this.getFxRestore();
10725             var w = this.getWidth(),
10726                 h = this.getHeight();
10727
10728             var st = this.dom.style;
10729
10730             var after = function(){
10731                 if(o.useDisplay){
10732                     el.setDisplayed(false);
10733                 }else{
10734                     el.hide();
10735                 }
10736
10737                 el.clearOpacity();
10738                 el.setPositioning(r.pos);
10739                 st.width = r.width;
10740                 st.height = r.height;
10741
10742                 el.afterFx(o);
10743             };
10744
10745             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10746             switch(anchor.toLowerCase()){
10747                 case "t":
10748                     pt.by = [0, -h];
10749                 break;
10750                 case "l":
10751                     pt.by = [-w, 0];
10752                 break;
10753                 case "r":
10754                     pt.by = [w, 0];
10755                 break;
10756                 case "b":
10757                     pt.by = [0, h];
10758                 break;
10759                 case "tl":
10760                     pt.by = [-w, -h];
10761                 break;
10762                 case "bl":
10763                     pt.by = [-w, h];
10764                 break;
10765                 case "br":
10766                     pt.by = [w, h];
10767                 break;
10768                 case "tr":
10769                     pt.by = [w, -h];
10770                 break;
10771             }
10772
10773             arguments.callee.anim = this.fxanim(a,
10774                 o,
10775                 'motion',
10776                 .5,
10777                 "easeOut", after);
10778         });
10779         return this;
10780     },
10781
10782         /**
10783          * Ensures that all effects queued after syncFx is called on the element are
10784          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10785          * @return {Roo.Element} The Element
10786          */
10787     syncFx : function(){
10788         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10789             block : false,
10790             concurrent : true,
10791             stopFx : false
10792         });
10793         return this;
10794     },
10795
10796         /**
10797          * Ensures that all effects queued after sequenceFx is called on the element are
10798          * run in sequence.  This is the opposite of {@link #syncFx}.
10799          * @return {Roo.Element} The Element
10800          */
10801     sequenceFx : function(){
10802         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10803             block : false,
10804             concurrent : false,
10805             stopFx : false
10806         });
10807         return this;
10808     },
10809
10810         /* @private */
10811     nextFx : function(){
10812         var ef = this.fxQueue[0];
10813         if(ef){
10814             ef.call(this);
10815         }
10816     },
10817
10818         /**
10819          * Returns true if the element has any effects actively running or queued, else returns false.
10820          * @return {Boolean} True if element has active effects, else false
10821          */
10822     hasActiveFx : function(){
10823         return this.fxQueue && this.fxQueue[0];
10824     },
10825
10826         /**
10827          * Stops any running effects and clears the element's internal effects queue if it contains
10828          * any additional effects that haven't started yet.
10829          * @return {Roo.Element} The Element
10830          */
10831     stopFx : function(){
10832         if(this.hasActiveFx()){
10833             var cur = this.fxQueue[0];
10834             if(cur && cur.anim && cur.anim.isAnimated()){
10835                 this.fxQueue = [cur]; // clear out others
10836                 cur.anim.stop(true);
10837             }
10838         }
10839         return this;
10840     },
10841
10842         /* @private */
10843     beforeFx : function(o){
10844         if(this.hasActiveFx() && !o.concurrent){
10845            if(o.stopFx){
10846                this.stopFx();
10847                return true;
10848            }
10849            return false;
10850         }
10851         return true;
10852     },
10853
10854         /**
10855          * Returns true if the element is currently blocking so that no other effect can be queued
10856          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10857          * used to ensure that an effect initiated by a user action runs to completion prior to the
10858          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10859          * @return {Boolean} True if blocking, else false
10860          */
10861     hasFxBlock : function(){
10862         var q = this.fxQueue;
10863         return q && q[0] && q[0].block;
10864     },
10865
10866         /* @private */
10867     queueFx : function(o, fn){
10868         if(!this.fxQueue){
10869             this.fxQueue = [];
10870         }
10871         if(!this.hasFxBlock()){
10872             Roo.applyIf(o, this.fxDefaults);
10873             if(!o.concurrent){
10874                 var run = this.beforeFx(o);
10875                 fn.block = o.block;
10876                 this.fxQueue.push(fn);
10877                 if(run){
10878                     this.nextFx();
10879                 }
10880             }else{
10881                 fn.call(this);
10882             }
10883         }
10884         return this;
10885     },
10886
10887         /* @private */
10888     fxWrap : function(pos, o, vis){
10889         var wrap;
10890         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10891             var wrapXY;
10892             if(o.fixPosition){
10893                 wrapXY = this.getXY();
10894             }
10895             var div = document.createElement("div");
10896             div.style.visibility = vis;
10897             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10898             wrap.setPositioning(pos);
10899             if(wrap.getStyle("position") == "static"){
10900                 wrap.position("relative");
10901             }
10902             this.clearPositioning('auto');
10903             wrap.clip();
10904             wrap.dom.appendChild(this.dom);
10905             if(wrapXY){
10906                 wrap.setXY(wrapXY);
10907             }
10908         }
10909         return wrap;
10910     },
10911
10912         /* @private */
10913     fxUnwrap : function(wrap, pos, o){
10914         this.clearPositioning();
10915         this.setPositioning(pos);
10916         if(!o.wrap){
10917             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10918             wrap.remove();
10919         }
10920     },
10921
10922         /* @private */
10923     getFxRestore : function(){
10924         var st = this.dom.style;
10925         return {pos: this.getPositioning(), width: st.width, height : st.height};
10926     },
10927
10928         /* @private */
10929     afterFx : function(o){
10930         if(o.afterStyle){
10931             this.applyStyles(o.afterStyle);
10932         }
10933         if(o.afterCls){
10934             this.addClass(o.afterCls);
10935         }
10936         if(o.remove === true){
10937             this.remove();
10938         }
10939         Roo.callback(o.callback, o.scope, [this]);
10940         if(!o.concurrent){
10941             this.fxQueue.shift();
10942             this.nextFx();
10943         }
10944     },
10945
10946         /* @private */
10947     getFxEl : function(){ // support for composite element fx
10948         return Roo.get(this.dom);
10949     },
10950
10951         /* @private */
10952     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10953         animType = animType || 'run';
10954         opt = opt || {};
10955         var anim = Roo.lib.Anim[animType](
10956             this.dom, args,
10957             (opt.duration || defaultDur) || .35,
10958             (opt.easing || defaultEase) || 'easeOut',
10959             function(){
10960                 Roo.callback(cb, this);
10961             },
10962             this
10963         );
10964         opt.anim = anim;
10965         return anim;
10966     }
10967 };
10968
10969 // backwords compat
10970 Roo.Fx.resize = Roo.Fx.scale;
10971
10972 //When included, Roo.Fx is automatically applied to Element so that all basic
10973 //effects are available directly via the Element API
10974 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10975  * Based on:
10976  * Ext JS Library 1.1.1
10977  * Copyright(c) 2006-2007, Ext JS, LLC.
10978  *
10979  * Originally Released Under LGPL - original licence link has changed is not relivant.
10980  *
10981  * Fork - LGPL
10982  * <script type="text/javascript">
10983  */
10984
10985
10986 /**
10987  * @class Roo.CompositeElement
10988  * Standard composite class. Creates a Roo.Element for every element in the collection.
10989  * <br><br>
10990  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10991  * actions will be performed on all the elements in this collection.</b>
10992  * <br><br>
10993  * All methods return <i>this</i> and can be chained.
10994  <pre><code>
10995  var els = Roo.select("#some-el div.some-class", true);
10996  // or select directly from an existing element
10997  var el = Roo.get('some-el');
10998  el.select('div.some-class', true);
10999
11000  els.setWidth(100); // all elements become 100 width
11001  els.hide(true); // all elements fade out and hide
11002  // or
11003  els.setWidth(100).hide(true);
11004  </code></pre>
11005  */
11006 Roo.CompositeElement = function(els){
11007     this.elements = [];
11008     this.addElements(els);
11009 };
11010 Roo.CompositeElement.prototype = {
11011     isComposite: true,
11012     addElements : function(els){
11013         if(!els) {
11014             return this;
11015         }
11016         if(typeof els == "string"){
11017             els = Roo.Element.selectorFunction(els);
11018         }
11019         var yels = this.elements;
11020         var index = yels.length-1;
11021         for(var i = 0, len = els.length; i < len; i++) {
11022                 yels[++index] = Roo.get(els[i]);
11023         }
11024         return this;
11025     },
11026
11027     /**
11028     * Clears this composite and adds the elements returned by the passed selector.
11029     * @param {String/Array} els A string CSS selector, an array of elements or an element
11030     * @return {CompositeElement} this
11031     */
11032     fill : function(els){
11033         this.elements = [];
11034         this.add(els);
11035         return this;
11036     },
11037
11038     /**
11039     * Filters this composite to only elements that match the passed selector.
11040     * @param {String} selector A string CSS selector
11041     * @param {Boolean} inverse return inverse filter (not matches)
11042     * @return {CompositeElement} this
11043     */
11044     filter : function(selector, inverse){
11045         var els = [];
11046         inverse = inverse || false;
11047         this.each(function(el){
11048             var match = inverse ? !el.is(selector) : el.is(selector);
11049             if(match){
11050                 els[els.length] = el.dom;
11051             }
11052         });
11053         this.fill(els);
11054         return this;
11055     },
11056
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         for(var i = 0, len = els.length; i < len; i++) {
11060                 Roo.Element.prototype[fn].apply(els[i], args);
11061         }
11062         return this;
11063     },
11064     /**
11065     * Adds elements to this composite.
11066     * @param {String/Array} els A string CSS selector, an array of elements or an element
11067     * @return {CompositeElement} this
11068     */
11069     add : function(els){
11070         if(typeof els == "string"){
11071             this.addElements(Roo.Element.selectorFunction(els));
11072         }else if(els.length !== undefined){
11073             this.addElements(els);
11074         }else{
11075             this.addElements([els]);
11076         }
11077         return this;
11078     },
11079     /**
11080     * Calls the passed function passing (el, this, index) for each element in this composite.
11081     * @param {Function} fn The function to call
11082     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11083     * @return {CompositeElement} this
11084     */
11085     each : function(fn, scope){
11086         var els = this.elements;
11087         for(var i = 0, len = els.length; i < len; i++){
11088             if(fn.call(scope || els[i], els[i], this, i) === false) {
11089                 break;
11090             }
11091         }
11092         return this;
11093     },
11094
11095     /**
11096      * Returns the Element object at the specified index
11097      * @param {Number} index
11098      * @return {Roo.Element}
11099      */
11100     item : function(index){
11101         return this.elements[index] || null;
11102     },
11103
11104     /**
11105      * Returns the first Element
11106      * @return {Roo.Element}
11107      */
11108     first : function(){
11109         return this.item(0);
11110     },
11111
11112     /**
11113      * Returns the last Element
11114      * @return {Roo.Element}
11115      */
11116     last : function(){
11117         return this.item(this.elements.length-1);
11118     },
11119
11120     /**
11121      * Returns the number of elements in this composite
11122      * @return Number
11123      */
11124     getCount : function(){
11125         return this.elements.length;
11126     },
11127
11128     /**
11129      * Returns true if this composite contains the passed element
11130      * @return Boolean
11131      */
11132     contains : function(el){
11133         return this.indexOf(el) !== -1;
11134     },
11135
11136     /**
11137      * Returns true if this composite contains the passed element
11138      * @return Boolean
11139      */
11140     indexOf : function(el){
11141         return this.elements.indexOf(Roo.get(el));
11142     },
11143
11144
11145     /**
11146     * Removes the specified element(s).
11147     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11148     * or an array of any of those.
11149     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11150     * @return {CompositeElement} this
11151     */
11152     removeElement : function(el, removeDom){
11153         if(el instanceof Array){
11154             for(var i = 0, len = el.length; i < len; i++){
11155                 this.removeElement(el[i]);
11156             }
11157             return this;
11158         }
11159         var index = typeof el == 'number' ? el : this.indexOf(el);
11160         if(index !== -1){
11161             if(removeDom){
11162                 var d = this.elements[index];
11163                 if(d.dom){
11164                     d.remove();
11165                 }else{
11166                     d.parentNode.removeChild(d);
11167                 }
11168             }
11169             this.elements.splice(index, 1);
11170         }
11171         return this;
11172     },
11173
11174     /**
11175     * Replaces the specified element with the passed element.
11176     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11177     * to replace.
11178     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11179     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11180     * @return {CompositeElement} this
11181     */
11182     replaceElement : function(el, replacement, domReplace){
11183         var index = typeof el == 'number' ? el : this.indexOf(el);
11184         if(index !== -1){
11185             if(domReplace){
11186                 this.elements[index].replaceWith(replacement);
11187             }else{
11188                 this.elements.splice(index, 1, Roo.get(replacement))
11189             }
11190         }
11191         return this;
11192     },
11193
11194     /**
11195      * Removes all elements.
11196      */
11197     clear : function(){
11198         this.elements = [];
11199     }
11200 };
11201 (function(){
11202     Roo.CompositeElement.createCall = function(proto, fnName){
11203         if(!proto[fnName]){
11204             proto[fnName] = function(){
11205                 return this.invoke(fnName, arguments);
11206             };
11207         }
11208     };
11209     for(var fnName in Roo.Element.prototype){
11210         if(typeof Roo.Element.prototype[fnName] == "function"){
11211             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11212         }
11213     };
11214 })();
11215 /*
11216  * Based on:
11217  * Ext JS Library 1.1.1
11218  * Copyright(c) 2006-2007, Ext JS, LLC.
11219  *
11220  * Originally Released Under LGPL - original licence link has changed is not relivant.
11221  *
11222  * Fork - LGPL
11223  * <script type="text/javascript">
11224  */
11225
11226 /**
11227  * @class Roo.CompositeElementLite
11228  * @extends Roo.CompositeElement
11229  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11230  <pre><code>
11231  var els = Roo.select("#some-el div.some-class");
11232  // or select directly from an existing element
11233  var el = Roo.get('some-el');
11234  el.select('div.some-class');
11235
11236  els.setWidth(100); // all elements become 100 width
11237  els.hide(true); // all elements fade out and hide
11238  // or
11239  els.setWidth(100).hide(true);
11240  </code></pre><br><br>
11241  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11242  * actions will be performed on all the elements in this collection.</b>
11243  */
11244 Roo.CompositeElementLite = function(els){
11245     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11246     this.el = new Roo.Element.Flyweight();
11247 };
11248 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11249     addElements : function(els){
11250         if(els){
11251             if(els instanceof Array){
11252                 this.elements = this.elements.concat(els);
11253             }else{
11254                 var yels = this.elements;
11255                 var index = yels.length-1;
11256                 for(var i = 0, len = els.length; i < len; i++) {
11257                     yels[++index] = els[i];
11258                 }
11259             }
11260         }
11261         return this;
11262     },
11263     invoke : function(fn, args){
11264         var els = this.elements;
11265         var el = this.el;
11266         for(var i = 0, len = els.length; i < len; i++) {
11267             el.dom = els[i];
11268                 Roo.Element.prototype[fn].apply(el, args);
11269         }
11270         return this;
11271     },
11272     /**
11273      * Returns a flyweight Element of the dom element object at the specified index
11274      * @param {Number} index
11275      * @return {Roo.Element}
11276      */
11277     item : function(index){
11278         if(!this.elements[index]){
11279             return null;
11280         }
11281         this.el.dom = this.elements[index];
11282         return this.el;
11283     },
11284
11285     // fixes scope with flyweight
11286     addListener : function(eventName, handler, scope, opt){
11287         var els = this.elements;
11288         for(var i = 0, len = els.length; i < len; i++) {
11289             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11290         }
11291         return this;
11292     },
11293
11294     /**
11295     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11296     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11297     * a reference to the dom node, use el.dom.</b>
11298     * @param {Function} fn The function to call
11299     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11300     * @return {CompositeElement} this
11301     */
11302     each : function(fn, scope){
11303         var els = this.elements;
11304         var el = this.el;
11305         for(var i = 0, len = els.length; i < len; i++){
11306             el.dom = els[i];
11307                 if(fn.call(scope || el, el, this, i) === false){
11308                 break;
11309             }
11310         }
11311         return this;
11312     },
11313
11314     indexOf : function(el){
11315         return this.elements.indexOf(Roo.getDom(el));
11316     },
11317
11318     replaceElement : function(el, replacement, domReplace){
11319         var index = typeof el == 'number' ? el : this.indexOf(el);
11320         if(index !== -1){
11321             replacement = Roo.getDom(replacement);
11322             if(domReplace){
11323                 var d = this.elements[index];
11324                 d.parentNode.insertBefore(replacement, d);
11325                 d.parentNode.removeChild(d);
11326             }
11327             this.elements.splice(index, 1, replacement);
11328         }
11329         return this;
11330     }
11331 });
11332 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11333
11334 /*
11335  * Based on:
11336  * Ext JS Library 1.1.1
11337  * Copyright(c) 2006-2007, Ext JS, LLC.
11338  *
11339  * Originally Released Under LGPL - original licence link has changed is not relivant.
11340  *
11341  * Fork - LGPL
11342  * <script type="text/javascript">
11343  */
11344
11345  
11346
11347 /**
11348  * @class Roo.data.Connection
11349  * @extends Roo.util.Observable
11350  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11351  * either to a configured URL, or to a URL specified at request time.<br><br>
11352  * <p>
11353  * Requests made by this class are asynchronous, and will return immediately. No data from
11354  * the server will be available to the statement immediately following the {@link #request} call.
11355  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11356  * <p>
11357  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11358  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11359  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11360  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11361  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11362  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11363  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11364  * standard DOM methods.
11365  * @constructor
11366  * @param {Object} config a configuration object.
11367  */
11368 Roo.data.Connection = function(config){
11369     Roo.apply(this, config);
11370     this.addEvents({
11371         /**
11372          * @event beforerequest
11373          * Fires before a network request is made to retrieve a data object.
11374          * @param {Connection} conn This Connection object.
11375          * @param {Object} options The options config object passed to the {@link #request} method.
11376          */
11377         "beforerequest" : true,
11378         /**
11379          * @event requestcomplete
11380          * Fires if the request was successfully completed.
11381          * @param {Connection} conn This Connection object.
11382          * @param {Object} response The XHR object containing the response data.
11383          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11384          * @param {Object} options The options config object passed to the {@link #request} method.
11385          */
11386         "requestcomplete" : true,
11387         /**
11388          * @event requestexception
11389          * Fires if an error HTTP status was returned from the server.
11390          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11391          * @param {Connection} conn This Connection object.
11392          * @param {Object} response The XHR object containing the response data.
11393          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11394          * @param {Object} options The options config object passed to the {@link #request} method.
11395          */
11396         "requestexception" : true
11397     });
11398     Roo.data.Connection.superclass.constructor.call(this);
11399 };
11400
11401 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11402     /**
11403      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11404      */
11405     /**
11406      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11407      * extra parameters to each request made by this object. (defaults to undefined)
11408      */
11409     /**
11410      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11411      *  to each request made by this object. (defaults to undefined)
11412      */
11413     /**
11414      * @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)
11415      */
11416     /**
11417      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11418      */
11419     timeout : 30000,
11420     /**
11421      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11422      * @type Boolean
11423      */
11424     autoAbort:false,
11425
11426     /**
11427      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11428      * @type Boolean
11429      */
11430     disableCaching: true,
11431
11432     /**
11433      * Sends an HTTP request to a remote server.
11434      * @param {Object} options An object which may contain the following properties:<ul>
11435      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11436      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11437      * request, a url encoded string or a function to call to get either.</li>
11438      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11439      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11440      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11441      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11442      * <li>options {Object} The parameter to the request call.</li>
11443      * <li>success {Boolean} True if the request succeeded.</li>
11444      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11445      * </ul></li>
11446      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11447      * The callback is passed the following parameters:<ul>
11448      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11449      * <li>options {Object} The parameter to the request call.</li>
11450      * </ul></li>
11451      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11452      * The callback is passed the following parameters:<ul>
11453      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11454      * <li>options {Object} The parameter to the request call.</li>
11455      * </ul></li>
11456      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11457      * for the callback function. Defaults to the browser window.</li>
11458      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11459      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11460      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11461      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11462      * params for the post data. Any params will be appended to the URL.</li>
11463      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11464      * </ul>
11465      * @return {Number} transactionId
11466      */
11467     request : function(o){
11468         if(this.fireEvent("beforerequest", this, o) !== false){
11469             var p = o.params;
11470
11471             if(typeof p == "function"){
11472                 p = p.call(o.scope||window, o);
11473             }
11474             if(typeof p == "object"){
11475                 p = Roo.urlEncode(o.params);
11476             }
11477             if(this.extraParams){
11478                 var extras = Roo.urlEncode(this.extraParams);
11479                 p = p ? (p + '&' + extras) : extras;
11480             }
11481
11482             var url = o.url || this.url;
11483             if(typeof url == 'function'){
11484                 url = url.call(o.scope||window, o);
11485             }
11486
11487             if(o.form){
11488                 var form = Roo.getDom(o.form);
11489                 url = url || form.action;
11490
11491                 var enctype = form.getAttribute("enctype");
11492                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11493                     return this.doFormUpload(o, p, url);
11494                 }
11495                 var f = Roo.lib.Ajax.serializeForm(form);
11496                 p = p ? (p + '&' + f) : f;
11497             }
11498
11499             var hs = o.headers;
11500             if(this.defaultHeaders){
11501                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11502                 if(!o.headers){
11503                     o.headers = hs;
11504                 }
11505             }
11506
11507             var cb = {
11508                 success: this.handleResponse,
11509                 failure: this.handleFailure,
11510                 scope: this,
11511                 argument: {options: o},
11512                 timeout : o.timeout || this.timeout
11513             };
11514
11515             var method = o.method||this.method||(p ? "POST" : "GET");
11516
11517             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11518                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11519             }
11520
11521             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11522                 if(o.autoAbort){
11523                     this.abort();
11524                 }
11525             }else if(this.autoAbort !== false){
11526                 this.abort();
11527             }
11528
11529             if((method == 'GET' && p) || o.xmlData){
11530                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11531                 p = '';
11532             }
11533             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11534             return this.transId;
11535         }else{
11536             Roo.callback(o.callback, o.scope, [o, null, null]);
11537             return null;
11538         }
11539     },
11540
11541     /**
11542      * Determine whether this object has a request outstanding.
11543      * @param {Number} transactionId (Optional) defaults to the last transaction
11544      * @return {Boolean} True if there is an outstanding request.
11545      */
11546     isLoading : function(transId){
11547         if(transId){
11548             return Roo.lib.Ajax.isCallInProgress(transId);
11549         }else{
11550             return this.transId ? true : false;
11551         }
11552     },
11553
11554     /**
11555      * Aborts any outstanding request.
11556      * @param {Number} transactionId (Optional) defaults to the last transaction
11557      */
11558     abort : function(transId){
11559         if(transId || this.isLoading()){
11560             Roo.lib.Ajax.abort(transId || this.transId);
11561         }
11562     },
11563
11564     // private
11565     handleResponse : function(response){
11566         this.transId = false;
11567         var options = response.argument.options;
11568         response.argument = options ? options.argument : null;
11569         this.fireEvent("requestcomplete", this, response, options);
11570         Roo.callback(options.success, options.scope, [response, options]);
11571         Roo.callback(options.callback, options.scope, [options, true, response]);
11572     },
11573
11574     // private
11575     handleFailure : function(response, e){
11576         this.transId = false;
11577         var options = response.argument.options;
11578         response.argument = options ? options.argument : null;
11579         this.fireEvent("requestexception", this, response, options, e);
11580         Roo.callback(options.failure, options.scope, [response, options]);
11581         Roo.callback(options.callback, options.scope, [options, false, response]);
11582     },
11583
11584     // private
11585     doFormUpload : function(o, ps, url){
11586         var id = Roo.id();
11587         var frame = document.createElement('iframe');
11588         frame.id = id;
11589         frame.name = id;
11590         frame.className = 'x-hidden';
11591         if(Roo.isIE){
11592             frame.src = Roo.SSL_SECURE_URL;
11593         }
11594         document.body.appendChild(frame);
11595
11596         if(Roo.isIE){
11597            document.frames[id].name = id;
11598         }
11599
11600         var form = Roo.getDom(o.form);
11601         form.target = id;
11602         form.method = 'POST';
11603         form.enctype = form.encoding = 'multipart/form-data';
11604         if(url){
11605             form.action = url;
11606         }
11607
11608         var hiddens, hd;
11609         if(ps){ // add dynamic params
11610             hiddens = [];
11611             ps = Roo.urlDecode(ps, false);
11612             for(var k in ps){
11613                 if(ps.hasOwnProperty(k)){
11614                     hd = document.createElement('input');
11615                     hd.type = 'hidden';
11616                     hd.name = k;
11617                     hd.value = ps[k];
11618                     form.appendChild(hd);
11619                     hiddens.push(hd);
11620                 }
11621             }
11622         }
11623
11624         function cb(){
11625             var r = {  // bogus response object
11626                 responseText : '',
11627                 responseXML : null
11628             };
11629
11630             r.argument = o ? o.argument : null;
11631
11632             try { //
11633                 var doc;
11634                 if(Roo.isIE){
11635                     doc = frame.contentWindow.document;
11636                 }else {
11637                     doc = (frame.contentDocument || window.frames[id].document);
11638                 }
11639                 if(doc && doc.body){
11640                     r.responseText = doc.body.innerHTML;
11641                 }
11642                 if(doc && doc.XMLDocument){
11643                     r.responseXML = doc.XMLDocument;
11644                 }else {
11645                     r.responseXML = doc;
11646                 }
11647             }
11648             catch(e) {
11649                 // ignore
11650             }
11651
11652             Roo.EventManager.removeListener(frame, 'load', cb, this);
11653
11654             this.fireEvent("requestcomplete", this, r, o);
11655             Roo.callback(o.success, o.scope, [r, o]);
11656             Roo.callback(o.callback, o.scope, [o, true, r]);
11657
11658             setTimeout(function(){document.body.removeChild(frame);}, 100);
11659         }
11660
11661         Roo.EventManager.on(frame, 'load', cb, this);
11662         form.submit();
11663
11664         if(hiddens){ // remove dynamic params
11665             for(var i = 0, len = hiddens.length; i < len; i++){
11666                 form.removeChild(hiddens[i]);
11667             }
11668         }
11669     }
11670 });
11671 /*
11672  * Based on:
11673  * Ext JS Library 1.1.1
11674  * Copyright(c) 2006-2007, Ext JS, LLC.
11675  *
11676  * Originally Released Under LGPL - original licence link has changed is not relivant.
11677  *
11678  * Fork - LGPL
11679  * <script type="text/javascript">
11680  */
11681  
11682 /**
11683  * Global Ajax request class.
11684  * 
11685  * @class Roo.Ajax
11686  * @extends Roo.data.Connection
11687  * @static
11688  * 
11689  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11690  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11691  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11692  * @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)
11693  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11694  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11695  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11696  */
11697 Roo.Ajax = new Roo.data.Connection({
11698     // fix up the docs
11699     /**
11700      * @scope Roo.Ajax
11701      * @type {Boolear} 
11702      */
11703     autoAbort : false,
11704
11705     /**
11706      * Serialize the passed form into a url encoded string
11707      * @scope Roo.Ajax
11708      * @param {String/HTMLElement} form
11709      * @return {String}
11710      */
11711     serializeForm : function(form){
11712         return Roo.lib.Ajax.serializeForm(form);
11713     }
11714 });/*
11715  * Based on:
11716  * Ext JS Library 1.1.1
11717  * Copyright(c) 2006-2007, Ext JS, LLC.
11718  *
11719  * Originally Released Under LGPL - original licence link has changed is not relivant.
11720  *
11721  * Fork - LGPL
11722  * <script type="text/javascript">
11723  */
11724
11725  
11726 /**
11727  * @class Roo.UpdateManager
11728  * @extends Roo.util.Observable
11729  * Provides AJAX-style update for Element object.<br><br>
11730  * Usage:<br>
11731  * <pre><code>
11732  * // Get it from a Roo.Element object
11733  * var el = Roo.get("foo");
11734  * var mgr = el.getUpdateManager();
11735  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11736  * ...
11737  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11738  * <br>
11739  * // or directly (returns the same UpdateManager instance)
11740  * var mgr = new Roo.UpdateManager("myElementId");
11741  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11742  * mgr.on("update", myFcnNeedsToKnow);
11743  * <br>
11744    // short handed call directly from the element object
11745    Roo.get("foo").load({
11746         url: "bar.php",
11747         scripts:true,
11748         params: "for=bar",
11749         text: "Loading Foo..."
11750    });
11751  * </code></pre>
11752  * @constructor
11753  * Create new UpdateManager directly.
11754  * @param {String/HTMLElement/Roo.Element} el The element to update
11755  * @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).
11756  */
11757 Roo.UpdateManager = function(el, forceNew){
11758     el = Roo.get(el);
11759     if(!forceNew && el.updateManager){
11760         return el.updateManager;
11761     }
11762     /**
11763      * The Element object
11764      * @type Roo.Element
11765      */
11766     this.el = el;
11767     /**
11768      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11769      * @type String
11770      */
11771     this.defaultUrl = null;
11772
11773     this.addEvents({
11774         /**
11775          * @event beforeupdate
11776          * Fired before an update is made, return false from your handler and the update is cancelled.
11777          * @param {Roo.Element} el
11778          * @param {String/Object/Function} url
11779          * @param {String/Object} params
11780          */
11781         "beforeupdate": true,
11782         /**
11783          * @event update
11784          * Fired after successful update is made.
11785          * @param {Roo.Element} el
11786          * @param {Object} oResponseObject The response Object
11787          */
11788         "update": true,
11789         /**
11790          * @event failure
11791          * Fired on update failure.
11792          * @param {Roo.Element} el
11793          * @param {Object} oResponseObject The response Object
11794          */
11795         "failure": true
11796     });
11797     var d = Roo.UpdateManager.defaults;
11798     /**
11799      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11800      * @type String
11801      */
11802     this.sslBlankUrl = d.sslBlankUrl;
11803     /**
11804      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11805      * @type Boolean
11806      */
11807     this.disableCaching = d.disableCaching;
11808     /**
11809      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11810      * @type String
11811      */
11812     this.indicatorText = d.indicatorText;
11813     /**
11814      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11815      * @type String
11816      */
11817     this.showLoadIndicator = d.showLoadIndicator;
11818     /**
11819      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11820      * @type Number
11821      */
11822     this.timeout = d.timeout;
11823
11824     /**
11825      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11826      * @type Boolean
11827      */
11828     this.loadScripts = d.loadScripts;
11829
11830     /**
11831      * Transaction object of current executing transaction
11832      */
11833     this.transaction = null;
11834
11835     /**
11836      * @private
11837      */
11838     this.autoRefreshProcId = null;
11839     /**
11840      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11841      * @type Function
11842      */
11843     this.refreshDelegate = this.refresh.createDelegate(this);
11844     /**
11845      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11846      * @type Function
11847      */
11848     this.updateDelegate = this.update.createDelegate(this);
11849     /**
11850      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11851      * @type Function
11852      */
11853     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11854     /**
11855      * @private
11856      */
11857     this.successDelegate = this.processSuccess.createDelegate(this);
11858     /**
11859      * @private
11860      */
11861     this.failureDelegate = this.processFailure.createDelegate(this);
11862
11863     if(!this.renderer){
11864      /**
11865       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11866       */
11867     this.renderer = new Roo.UpdateManager.BasicRenderer();
11868     }
11869     
11870     Roo.UpdateManager.superclass.constructor.call(this);
11871 };
11872
11873 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11874     /**
11875      * Get the Element this UpdateManager is bound to
11876      * @return {Roo.Element} The element
11877      */
11878     getEl : function(){
11879         return this.el;
11880     },
11881     /**
11882      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11883      * @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:
11884 <pre><code>
11885 um.update({<br/>
11886     url: "your-url.php",<br/>
11887     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11888     callback: yourFunction,<br/>
11889     scope: yourObject, //(optional scope)  <br/>
11890     discardUrl: false, <br/>
11891     nocache: false,<br/>
11892     text: "Loading...",<br/>
11893     timeout: 30,<br/>
11894     scripts: false<br/>
11895 });
11896 </code></pre>
11897      * The only required property is url. The optional properties nocache, text and scripts
11898      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11899      * @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}
11900      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11901      * @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.
11902      */
11903     update : function(url, params, callback, discardUrl){
11904         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11905             var method = this.method,
11906                 cfg;
11907             if(typeof url == "object"){ // must be config object
11908                 cfg = url;
11909                 url = cfg.url;
11910                 params = params || cfg.params;
11911                 callback = callback || cfg.callback;
11912                 discardUrl = discardUrl || cfg.discardUrl;
11913                 if(callback && cfg.scope){
11914                     callback = callback.createDelegate(cfg.scope);
11915                 }
11916                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11917                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11918                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11919                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11920                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11921             }
11922             this.showLoading();
11923             if(!discardUrl){
11924                 this.defaultUrl = url;
11925             }
11926             if(typeof url == "function"){
11927                 url = url.call(this);
11928             }
11929
11930             method = method || (params ? "POST" : "GET");
11931             if(method == "GET"){
11932                 url = this.prepareUrl(url);
11933             }
11934
11935             var o = Roo.apply(cfg ||{}, {
11936                 url : url,
11937                 params: params,
11938                 success: this.successDelegate,
11939                 failure: this.failureDelegate,
11940                 callback: undefined,
11941                 timeout: (this.timeout*1000),
11942                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11943             });
11944             Roo.log("updated manager called with timeout of " + o.timeout);
11945             this.transaction = Roo.Ajax.request(o);
11946         }
11947     },
11948
11949     /**
11950      * 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.
11951      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11952      * @param {String/HTMLElement} form The form Id or form element
11953      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11954      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11955      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11956      */
11957     formUpdate : function(form, url, reset, callback){
11958         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11959             if(typeof url == "function"){
11960                 url = url.call(this);
11961             }
11962             form = Roo.getDom(form);
11963             this.transaction = Roo.Ajax.request({
11964                 form: form,
11965                 url:url,
11966                 success: this.successDelegate,
11967                 failure: this.failureDelegate,
11968                 timeout: (this.timeout*1000),
11969                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11970             });
11971             this.showLoading.defer(1, this);
11972         }
11973     },
11974
11975     /**
11976      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11977      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11978      */
11979     refresh : function(callback){
11980         if(this.defaultUrl == null){
11981             return;
11982         }
11983         this.update(this.defaultUrl, null, callback, true);
11984     },
11985
11986     /**
11987      * Set this element to auto refresh.
11988      * @param {Number} interval How often to update (in seconds).
11989      * @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)
11990      * @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}
11991      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11992      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11993      */
11994     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11995         if(refreshNow){
11996             this.update(url || this.defaultUrl, params, callback, true);
11997         }
11998         if(this.autoRefreshProcId){
11999             clearInterval(this.autoRefreshProcId);
12000         }
12001         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12002     },
12003
12004     /**
12005      * Stop auto refresh on this element.
12006      */
12007      stopAutoRefresh : function(){
12008         if(this.autoRefreshProcId){
12009             clearInterval(this.autoRefreshProcId);
12010             delete this.autoRefreshProcId;
12011         }
12012     },
12013
12014     isAutoRefreshing : function(){
12015        return this.autoRefreshProcId ? true : false;
12016     },
12017     /**
12018      * Called to update the element to "Loading" state. Override to perform custom action.
12019      */
12020     showLoading : function(){
12021         if(this.showLoadIndicator){
12022             this.el.update(this.indicatorText);
12023         }
12024     },
12025
12026     /**
12027      * Adds unique parameter to query string if disableCaching = true
12028      * @private
12029      */
12030     prepareUrl : function(url){
12031         if(this.disableCaching){
12032             var append = "_dc=" + (new Date().getTime());
12033             if(url.indexOf("?") !== -1){
12034                 url += "&" + append;
12035             }else{
12036                 url += "?" + append;
12037             }
12038         }
12039         return url;
12040     },
12041
12042     /**
12043      * @private
12044      */
12045     processSuccess : function(response){
12046         this.transaction = null;
12047         if(response.argument.form && response.argument.reset){
12048             try{ // put in try/catch since some older FF releases had problems with this
12049                 response.argument.form.reset();
12050             }catch(e){}
12051         }
12052         if(this.loadScripts){
12053             this.renderer.render(this.el, response, this,
12054                 this.updateComplete.createDelegate(this, [response]));
12055         }else{
12056             this.renderer.render(this.el, response, this);
12057             this.updateComplete(response);
12058         }
12059     },
12060
12061     updateComplete : function(response){
12062         this.fireEvent("update", this.el, response);
12063         if(typeof response.argument.callback == "function"){
12064             response.argument.callback(this.el, true, response);
12065         }
12066     },
12067
12068     /**
12069      * @private
12070      */
12071     processFailure : function(response){
12072         this.transaction = null;
12073         this.fireEvent("failure", this.el, response);
12074         if(typeof response.argument.callback == "function"){
12075             response.argument.callback(this.el, false, response);
12076         }
12077     },
12078
12079     /**
12080      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12081      * @param {Object} renderer The object implementing the render() method
12082      */
12083     setRenderer : function(renderer){
12084         this.renderer = renderer;
12085     },
12086
12087     getRenderer : function(){
12088        return this.renderer;
12089     },
12090
12091     /**
12092      * Set the defaultUrl used for updates
12093      * @param {String/Function} defaultUrl The url or a function to call to get the url
12094      */
12095     setDefaultUrl : function(defaultUrl){
12096         this.defaultUrl = defaultUrl;
12097     },
12098
12099     /**
12100      * Aborts the executing transaction
12101      */
12102     abort : function(){
12103         if(this.transaction){
12104             Roo.Ajax.abort(this.transaction);
12105         }
12106     },
12107
12108     /**
12109      * Returns true if an update is in progress
12110      * @return {Boolean}
12111      */
12112     isUpdating : function(){
12113         if(this.transaction){
12114             return Roo.Ajax.isLoading(this.transaction);
12115         }
12116         return false;
12117     }
12118 });
12119
12120 /**
12121  * @class Roo.UpdateManager.defaults
12122  * @static (not really - but it helps the doc tool)
12123  * The defaults collection enables customizing the default properties of UpdateManager
12124  */
12125    Roo.UpdateManager.defaults = {
12126        /**
12127          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12128          * @type Number
12129          */
12130          timeout : 30,
12131
12132          /**
12133          * True to process scripts by default (Defaults to false).
12134          * @type Boolean
12135          */
12136         loadScripts : false,
12137
12138         /**
12139         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12140         * @type String
12141         */
12142         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12143         /**
12144          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12145          * @type Boolean
12146          */
12147         disableCaching : false,
12148         /**
12149          * Whether to show indicatorText when loading (Defaults to true).
12150          * @type Boolean
12151          */
12152         showLoadIndicator : true,
12153         /**
12154          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12155          * @type String
12156          */
12157         indicatorText : '<div class="loading-indicator">Loading...</div>'
12158    };
12159
12160 /**
12161  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12162  *Usage:
12163  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12164  * @param {String/HTMLElement/Roo.Element} el The element to update
12165  * @param {String} url The url
12166  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12167  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12168  * @static
12169  * @deprecated
12170  * @member Roo.UpdateManager
12171  */
12172 Roo.UpdateManager.updateElement = function(el, url, params, options){
12173     var um = Roo.get(el, true).getUpdateManager();
12174     Roo.apply(um, options);
12175     um.update(url, params, options ? options.callback : null);
12176 };
12177 // alias for backwards compat
12178 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12179 /**
12180  * @class Roo.UpdateManager.BasicRenderer
12181  * Default Content renderer. Updates the elements innerHTML with the responseText.
12182  */
12183 Roo.UpdateManager.BasicRenderer = function(){};
12184
12185 Roo.UpdateManager.BasicRenderer.prototype = {
12186     /**
12187      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12188      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12189      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12190      * @param {Roo.Element} el The element being rendered
12191      * @param {Object} response The YUI Connect response object
12192      * @param {UpdateManager} updateManager The calling update manager
12193      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12194      */
12195      render : function(el, response, updateManager, callback){
12196         el.update(response.responseText, updateManager.loadScripts, callback);
12197     }
12198 };
12199 /*
12200  * Based on:
12201  * Roo JS
12202  * (c)) Alan Knowles
12203  * Licence : LGPL
12204  */
12205
12206
12207 /**
12208  * @class Roo.DomTemplate
12209  * @extends Roo.Template
12210  * An effort at a dom based template engine..
12211  *
12212  * Similar to XTemplate, except it uses dom parsing to create the template..
12213  *
12214  * Supported features:
12215  *
12216  *  Tags:
12217
12218 <pre><code>
12219       {a_variable} - output encoded.
12220       {a_variable.format:("Y-m-d")} - call a method on the variable
12221       {a_variable:raw} - unencoded output
12222       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12223       {a_variable:this.method_on_template(...)} - call a method on the template object.
12224  
12225 </code></pre>
12226  *  The tpl tag:
12227 <pre><code>
12228         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12229         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12230         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12231         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12232   
12233 </code></pre>
12234  *      
12235  */
12236 Roo.DomTemplate = function()
12237 {
12238      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12239      if (this.html) {
12240         this.compile();
12241      }
12242 };
12243
12244
12245 Roo.extend(Roo.DomTemplate, Roo.Template, {
12246     /**
12247      * id counter for sub templates.
12248      */
12249     id : 0,
12250     /**
12251      * flag to indicate if dom parser is inside a pre,
12252      * it will strip whitespace if not.
12253      */
12254     inPre : false,
12255     
12256     /**
12257      * The various sub templates
12258      */
12259     tpls : false,
12260     
12261     
12262     
12263     /**
12264      *
12265      * basic tag replacing syntax
12266      * WORD:WORD()
12267      *
12268      * // you can fake an object call by doing this
12269      *  x.t:(test,tesT) 
12270      * 
12271      */
12272     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12273     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12274     
12275     iterChild : function (node, method) {
12276         
12277         var oldPre = this.inPre;
12278         if (node.tagName == 'PRE') {
12279             this.inPre = true;
12280         }
12281         for( var i = 0; i < node.childNodes.length; i++) {
12282             method.call(this, node.childNodes[i]);
12283         }
12284         this.inPre = oldPre;
12285     },
12286     
12287     
12288     
12289     /**
12290      * compile the template
12291      *
12292      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12293      *
12294      */
12295     compile: function()
12296     {
12297         var s = this.html;
12298         
12299         // covert the html into DOM...
12300         var doc = false;
12301         var div =false;
12302         try {
12303             doc = document.implementation.createHTMLDocument("");
12304             doc.documentElement.innerHTML =   this.html  ;
12305             div = doc.documentElement;
12306         } catch (e) {
12307             // old IE... - nasty -- it causes all sorts of issues.. with
12308             // images getting pulled from server..
12309             div = document.createElement('div');
12310             div.innerHTML = this.html;
12311         }
12312         //doc.documentElement.innerHTML = htmlBody
12313          
12314         
12315         
12316         this.tpls = [];
12317         var _t = this;
12318         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12319         
12320         var tpls = this.tpls;
12321         
12322         // create a top level template from the snippet..
12323         
12324         //Roo.log(div.innerHTML);
12325         
12326         var tpl = {
12327             uid : 'master',
12328             id : this.id++,
12329             attr : false,
12330             value : false,
12331             body : div.innerHTML,
12332             
12333             forCall : false,
12334             execCall : false,
12335             dom : div,
12336             isTop : true
12337             
12338         };
12339         tpls.unshift(tpl);
12340         
12341         
12342         // compile them...
12343         this.tpls = [];
12344         Roo.each(tpls, function(tp){
12345             this.compileTpl(tp);
12346             this.tpls[tp.id] = tp;
12347         }, this);
12348         
12349         this.master = tpls[0];
12350         return this;
12351         
12352         
12353     },
12354     
12355     compileNode : function(node, istop) {
12356         // test for
12357         //Roo.log(node);
12358         
12359         
12360         // skip anything not a tag..
12361         if (node.nodeType != 1) {
12362             if (node.nodeType == 3 && !this.inPre) {
12363                 // reduce white space..
12364                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12365                 
12366             }
12367             return;
12368         }
12369         
12370         var tpl = {
12371             uid : false,
12372             id : false,
12373             attr : false,
12374             value : false,
12375             body : '',
12376             
12377             forCall : false,
12378             execCall : false,
12379             dom : false,
12380             isTop : istop
12381             
12382             
12383         };
12384         
12385         
12386         switch(true) {
12387             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12388             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12389             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12390             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12391             // no default..
12392         }
12393         
12394         
12395         if (!tpl.attr) {
12396             // just itterate children..
12397             this.iterChild(node,this.compileNode);
12398             return;
12399         }
12400         tpl.uid = this.id++;
12401         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12402         node.removeAttribute('roo-'+ tpl.attr);
12403         if (tpl.attr != 'name') {
12404             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12405             node.parentNode.replaceChild(placeholder,  node);
12406         } else {
12407             
12408             var placeholder =  document.createElement('span');
12409             placeholder.className = 'roo-tpl-' + tpl.value;
12410             node.parentNode.replaceChild(placeholder,  node);
12411         }
12412         
12413         // parent now sees '{domtplXXXX}
12414         this.iterChild(node,this.compileNode);
12415         
12416         // we should now have node body...
12417         var div = document.createElement('div');
12418         div.appendChild(node);
12419         tpl.dom = node;
12420         // this has the unfortunate side effect of converting tagged attributes
12421         // eg. href="{...}" into %7C...%7D
12422         // this has been fixed by searching for those combo's although it's a bit hacky..
12423         
12424         
12425         tpl.body = div.innerHTML;
12426         
12427         
12428          
12429         tpl.id = tpl.uid;
12430         switch(tpl.attr) {
12431             case 'for' :
12432                 switch (tpl.value) {
12433                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12434                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12435                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12436                 }
12437                 break;
12438             
12439             case 'exec':
12440                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12441                 break;
12442             
12443             case 'if':     
12444                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12445                 break;
12446             
12447             case 'name':
12448                 tpl.id  = tpl.value; // replace non characters???
12449                 break;
12450             
12451         }
12452         
12453         
12454         this.tpls.push(tpl);
12455         
12456         
12457         
12458     },
12459     
12460     
12461     
12462     
12463     /**
12464      * Compile a segment of the template into a 'sub-template'
12465      *
12466      * 
12467      * 
12468      *
12469      */
12470     compileTpl : function(tpl)
12471     {
12472         var fm = Roo.util.Format;
12473         var useF = this.disableFormats !== true;
12474         
12475         var sep = Roo.isGecko ? "+\n" : ",\n";
12476         
12477         var undef = function(str) {
12478             Roo.debug && Roo.log("Property not found :"  + str);
12479             return '';
12480         };
12481           
12482         //Roo.log(tpl.body);
12483         
12484         
12485         
12486         var fn = function(m, lbrace, name, format, args)
12487         {
12488             //Roo.log("ARGS");
12489             //Roo.log(arguments);
12490             args = args ? args.replace(/\\'/g,"'") : args;
12491             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12492             if (typeof(format) == 'undefined') {
12493                 format =  'htmlEncode'; 
12494             }
12495             if (format == 'raw' ) {
12496                 format = false;
12497             }
12498             
12499             if(name.substr(0, 6) == 'domtpl'){
12500                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12501             }
12502             
12503             // build an array of options to determine if value is undefined..
12504             
12505             // basically get 'xxxx.yyyy' then do
12506             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12507             //    (function () { Roo.log("Property not found"); return ''; })() :
12508             //    ......
12509             
12510             var udef_ar = [];
12511             var lookfor = '';
12512             Roo.each(name.split('.'), function(st) {
12513                 lookfor += (lookfor.length ? '.': '') + st;
12514                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12515             });
12516             
12517             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12518             
12519             
12520             if(format && useF){
12521                 
12522                 args = args ? ',' + args : "";
12523                  
12524                 if(format.substr(0, 5) != "this."){
12525                     format = "fm." + format + '(';
12526                 }else{
12527                     format = 'this.call("'+ format.substr(5) + '", ';
12528                     args = ", values";
12529                 }
12530                 
12531                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12532             }
12533              
12534             if (args && args.length) {
12535                 // called with xxyx.yuu:(test,test)
12536                 // change to ()
12537                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12538             }
12539             // raw.. - :raw modifier..
12540             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12541             
12542         };
12543         var body;
12544         // branched to use + in gecko and [].join() in others
12545         if(Roo.isGecko){
12546             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12547                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12548                     "';};};";
12549         }else{
12550             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12551             body.push(tpl.body.replace(/(\r\n|\n)/g,
12552                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12553             body.push("'].join('');};};");
12554             body = body.join('');
12555         }
12556         
12557         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12558        
12559         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12560         eval(body);
12561         
12562         return this;
12563     },
12564      
12565     /**
12566      * same as applyTemplate, except it's done to one of the subTemplates
12567      * when using named templates, you can do:
12568      *
12569      * var str = pl.applySubTemplate('your-name', values);
12570      *
12571      * 
12572      * @param {Number} id of the template
12573      * @param {Object} values to apply to template
12574      * @param {Object} parent (normaly the instance of this object)
12575      */
12576     applySubTemplate : function(id, values, parent)
12577     {
12578         
12579         
12580         var t = this.tpls[id];
12581         
12582         
12583         try { 
12584             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12585                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12586                 return '';
12587             }
12588         } catch(e) {
12589             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12590             Roo.log(values);
12591           
12592             return '';
12593         }
12594         try { 
12595             
12596             if(t.execCall && t.execCall.call(this, values, parent)){
12597                 return '';
12598             }
12599         } catch(e) {
12600             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12601             Roo.log(values);
12602             return '';
12603         }
12604         
12605         try {
12606             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12607             parent = t.target ? values : parent;
12608             if(t.forCall && vs instanceof Array){
12609                 var buf = [];
12610                 for(var i = 0, len = vs.length; i < len; i++){
12611                     try {
12612                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12613                     } catch (e) {
12614                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12615                         Roo.log(e.body);
12616                         //Roo.log(t.compiled);
12617                         Roo.log(vs[i]);
12618                     }   
12619                 }
12620                 return buf.join('');
12621             }
12622         } catch (e) {
12623             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12624             Roo.log(values);
12625             return '';
12626         }
12627         try {
12628             return t.compiled.call(this, vs, parent);
12629         } catch (e) {
12630             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12631             Roo.log(e.body);
12632             //Roo.log(t.compiled);
12633             Roo.log(values);
12634             return '';
12635         }
12636     },
12637
12638    
12639
12640     applyTemplate : function(values){
12641         return this.master.compiled.call(this, values, {});
12642         //var s = this.subs;
12643     },
12644
12645     apply : function(){
12646         return this.applyTemplate.apply(this, arguments);
12647     }
12648
12649  });
12650
12651 Roo.DomTemplate.from = function(el){
12652     el = Roo.getDom(el);
12653     return new Roo.Domtemplate(el.value || el.innerHTML);
12654 };/*
12655  * Based on:
12656  * Ext JS Library 1.1.1
12657  * Copyright(c) 2006-2007, Ext JS, LLC.
12658  *
12659  * Originally Released Under LGPL - original licence link has changed is not relivant.
12660  *
12661  * Fork - LGPL
12662  * <script type="text/javascript">
12663  */
12664
12665 /**
12666  * @class Roo.util.DelayedTask
12667  * Provides a convenient method of performing setTimeout where a new
12668  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12669  * You can use this class to buffer
12670  * the keypress events for a certain number of milliseconds, and perform only if they stop
12671  * for that amount of time.
12672  * @constructor The parameters to this constructor serve as defaults and are not required.
12673  * @param {Function} fn (optional) The default function to timeout
12674  * @param {Object} scope (optional) The default scope of that timeout
12675  * @param {Array} args (optional) The default Array of arguments
12676  */
12677 Roo.util.DelayedTask = function(fn, scope, args){
12678     var id = null, d, t;
12679
12680     var call = function(){
12681         var now = new Date().getTime();
12682         if(now - t >= d){
12683             clearInterval(id);
12684             id = null;
12685             fn.apply(scope, args || []);
12686         }
12687     };
12688     /**
12689      * Cancels any pending timeout and queues a new one
12690      * @param {Number} delay The milliseconds to delay
12691      * @param {Function} newFn (optional) Overrides function passed to constructor
12692      * @param {Object} newScope (optional) Overrides scope passed to constructor
12693      * @param {Array} newArgs (optional) Overrides args passed to constructor
12694      */
12695     this.delay = function(delay, newFn, newScope, newArgs){
12696         if(id && delay != d){
12697             this.cancel();
12698         }
12699         d = delay;
12700         t = new Date().getTime();
12701         fn = newFn || fn;
12702         scope = newScope || scope;
12703         args = newArgs || args;
12704         if(!id){
12705             id = setInterval(call, d);
12706         }
12707     };
12708
12709     /**
12710      * Cancel the last queued timeout
12711      */
12712     this.cancel = function(){
12713         if(id){
12714             clearInterval(id);
12715             id = null;
12716         }
12717     };
12718 };/*
12719  * Based on:
12720  * Ext JS Library 1.1.1
12721  * Copyright(c) 2006-2007, Ext JS, LLC.
12722  *
12723  * Originally Released Under LGPL - original licence link has changed is not relivant.
12724  *
12725  * Fork - LGPL
12726  * <script type="text/javascript">
12727  */
12728  
12729  
12730 Roo.util.TaskRunner = function(interval){
12731     interval = interval || 10;
12732     var tasks = [], removeQueue = [];
12733     var id = 0;
12734     var running = false;
12735
12736     var stopThread = function(){
12737         running = false;
12738         clearInterval(id);
12739         id = 0;
12740     };
12741
12742     var startThread = function(){
12743         if(!running){
12744             running = true;
12745             id = setInterval(runTasks, interval);
12746         }
12747     };
12748
12749     var removeTask = function(task){
12750         removeQueue.push(task);
12751         if(task.onStop){
12752             task.onStop();
12753         }
12754     };
12755
12756     var runTasks = function(){
12757         if(removeQueue.length > 0){
12758             for(var i = 0, len = removeQueue.length; i < len; i++){
12759                 tasks.remove(removeQueue[i]);
12760             }
12761             removeQueue = [];
12762             if(tasks.length < 1){
12763                 stopThread();
12764                 return;
12765             }
12766         }
12767         var now = new Date().getTime();
12768         for(var i = 0, len = tasks.length; i < len; ++i){
12769             var t = tasks[i];
12770             var itime = now - t.taskRunTime;
12771             if(t.interval <= itime){
12772                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12773                 t.taskRunTime = now;
12774                 if(rt === false || t.taskRunCount === t.repeat){
12775                     removeTask(t);
12776                     return;
12777                 }
12778             }
12779             if(t.duration && t.duration <= (now - t.taskStartTime)){
12780                 removeTask(t);
12781             }
12782         }
12783     };
12784
12785     /**
12786      * Queues a new task.
12787      * @param {Object} task
12788      */
12789     this.start = function(task){
12790         tasks.push(task);
12791         task.taskStartTime = new Date().getTime();
12792         task.taskRunTime = 0;
12793         task.taskRunCount = 0;
12794         startThread();
12795         return task;
12796     };
12797
12798     this.stop = function(task){
12799         removeTask(task);
12800         return task;
12801     };
12802
12803     this.stopAll = function(){
12804         stopThread();
12805         for(var i = 0, len = tasks.length; i < len; i++){
12806             if(tasks[i].onStop){
12807                 tasks[i].onStop();
12808             }
12809         }
12810         tasks = [];
12811         removeQueue = [];
12812     };
12813 };
12814
12815 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12816  * Based on:
12817  * Ext JS Library 1.1.1
12818  * Copyright(c) 2006-2007, Ext JS, LLC.
12819  *
12820  * Originally Released Under LGPL - original licence link has changed is not relivant.
12821  *
12822  * Fork - LGPL
12823  * <script type="text/javascript">
12824  */
12825
12826  
12827 /**
12828  * @class Roo.util.MixedCollection
12829  * @extends Roo.util.Observable
12830  * A Collection class that maintains both numeric indexes and keys and exposes events.
12831  * @constructor
12832  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12833  * collection (defaults to false)
12834  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12835  * and return the key value for that item.  This is used when available to look up the key on items that
12836  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12837  * equivalent to providing an implementation for the {@link #getKey} method.
12838  */
12839 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12840     this.items = [];
12841     this.map = {};
12842     this.keys = [];
12843     this.length = 0;
12844     this.addEvents({
12845         /**
12846          * @event clear
12847          * Fires when the collection is cleared.
12848          */
12849         "clear" : true,
12850         /**
12851          * @event add
12852          * Fires when an item is added to the collection.
12853          * @param {Number} index The index at which the item was added.
12854          * @param {Object} o The item added.
12855          * @param {String} key The key associated with the added item.
12856          */
12857         "add" : true,
12858         /**
12859          * @event replace
12860          * Fires when an item is replaced in the collection.
12861          * @param {String} key he key associated with the new added.
12862          * @param {Object} old The item being replaced.
12863          * @param {Object} new The new item.
12864          */
12865         "replace" : true,
12866         /**
12867          * @event remove
12868          * Fires when an item is removed from the collection.
12869          * @param {Object} o The item being removed.
12870          * @param {String} key (optional) The key associated with the removed item.
12871          */
12872         "remove" : true,
12873         "sort" : true
12874     });
12875     this.allowFunctions = allowFunctions === true;
12876     if(keyFn){
12877         this.getKey = keyFn;
12878     }
12879     Roo.util.MixedCollection.superclass.constructor.call(this);
12880 };
12881
12882 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12883     allowFunctions : false,
12884     
12885 /**
12886  * Adds an item to the collection.
12887  * @param {String} key The key to associate with the item
12888  * @param {Object} o The item to add.
12889  * @return {Object} The item added.
12890  */
12891     add : function(key, o){
12892         if(arguments.length == 1){
12893             o = arguments[0];
12894             key = this.getKey(o);
12895         }
12896         if(typeof key == "undefined" || key === null){
12897             this.length++;
12898             this.items.push(o);
12899             this.keys.push(null);
12900         }else{
12901             var old = this.map[key];
12902             if(old){
12903                 return this.replace(key, o);
12904             }
12905             this.length++;
12906             this.items.push(o);
12907             this.map[key] = o;
12908             this.keys.push(key);
12909         }
12910         this.fireEvent("add", this.length-1, o, key);
12911         return o;
12912     },
12913        
12914 /**
12915   * MixedCollection has a generic way to fetch keys if you implement getKey.
12916 <pre><code>
12917 // normal way
12918 var mc = new Roo.util.MixedCollection();
12919 mc.add(someEl.dom.id, someEl);
12920 mc.add(otherEl.dom.id, otherEl);
12921 //and so on
12922
12923 // using getKey
12924 var mc = new Roo.util.MixedCollection();
12925 mc.getKey = function(el){
12926    return el.dom.id;
12927 };
12928 mc.add(someEl);
12929 mc.add(otherEl);
12930
12931 // or via the constructor
12932 var mc = new Roo.util.MixedCollection(false, function(el){
12933    return el.dom.id;
12934 });
12935 mc.add(someEl);
12936 mc.add(otherEl);
12937 </code></pre>
12938  * @param o {Object} The item for which to find the key.
12939  * @return {Object} The key for the passed item.
12940  */
12941     getKey : function(o){
12942          return o.id; 
12943     },
12944    
12945 /**
12946  * Replaces an item in the collection.
12947  * @param {String} key The key associated with the item to replace, or the item to replace.
12948  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12949  * @return {Object}  The new item.
12950  */
12951     replace : function(key, o){
12952         if(arguments.length == 1){
12953             o = arguments[0];
12954             key = this.getKey(o);
12955         }
12956         var old = this.item(key);
12957         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12958              return this.add(key, o);
12959         }
12960         var index = this.indexOfKey(key);
12961         this.items[index] = o;
12962         this.map[key] = o;
12963         this.fireEvent("replace", key, old, o);
12964         return o;
12965     },
12966    
12967 /**
12968  * Adds all elements of an Array or an Object to the collection.
12969  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12970  * an Array of values, each of which are added to the collection.
12971  */
12972     addAll : function(objs){
12973         if(arguments.length > 1 || objs instanceof Array){
12974             var args = arguments.length > 1 ? arguments : objs;
12975             for(var i = 0, len = args.length; i < len; i++){
12976                 this.add(args[i]);
12977             }
12978         }else{
12979             for(var key in objs){
12980                 if(this.allowFunctions || typeof objs[key] != "function"){
12981                     this.add(key, objs[key]);
12982                 }
12983             }
12984         }
12985     },
12986    
12987 /**
12988  * Executes the specified function once for every item in the collection, passing each
12989  * item as the first and only parameter. returning false from the function will stop the iteration.
12990  * @param {Function} fn The function to execute for each item.
12991  * @param {Object} scope (optional) The scope in which to execute the function.
12992  */
12993     each : function(fn, scope){
12994         var items = [].concat(this.items); // each safe for removal
12995         for(var i = 0, len = items.length; i < len; i++){
12996             if(fn.call(scope || items[i], items[i], i, len) === false){
12997                 break;
12998             }
12999         }
13000     },
13001    
13002 /**
13003  * Executes the specified function once for every key in the collection, passing each
13004  * key, and its associated item as the first two parameters.
13005  * @param {Function} fn The function to execute for each item.
13006  * @param {Object} scope (optional) The scope in which to execute the function.
13007  */
13008     eachKey : function(fn, scope){
13009         for(var i = 0, len = this.keys.length; i < len; i++){
13010             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13011         }
13012     },
13013    
13014 /**
13015  * Returns the first item in the collection which elicits a true return value from the
13016  * passed selection function.
13017  * @param {Function} fn The selection function to execute for each item.
13018  * @param {Object} scope (optional) The scope in which to execute the function.
13019  * @return {Object} The first item in the collection which returned true from the selection function.
13020  */
13021     find : function(fn, scope){
13022         for(var i = 0, len = this.items.length; i < len; i++){
13023             if(fn.call(scope || window, this.items[i], this.keys[i])){
13024                 return this.items[i];
13025             }
13026         }
13027         return null;
13028     },
13029    
13030 /**
13031  * Inserts an item at the specified index in the collection.
13032  * @param {Number} index The index to insert the item at.
13033  * @param {String} key The key to associate with the new item, or the item itself.
13034  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13035  * @return {Object} The item inserted.
13036  */
13037     insert : function(index, key, o){
13038         if(arguments.length == 2){
13039             o = arguments[1];
13040             key = this.getKey(o);
13041         }
13042         if(index >= this.length){
13043             return this.add(key, o);
13044         }
13045         this.length++;
13046         this.items.splice(index, 0, o);
13047         if(typeof key != "undefined" && key != null){
13048             this.map[key] = o;
13049         }
13050         this.keys.splice(index, 0, key);
13051         this.fireEvent("add", index, o, key);
13052         return o;
13053     },
13054    
13055 /**
13056  * Removed an item from the collection.
13057  * @param {Object} o The item to remove.
13058  * @return {Object} The item removed.
13059  */
13060     remove : function(o){
13061         return this.removeAt(this.indexOf(o));
13062     },
13063    
13064 /**
13065  * Remove an item from a specified index in the collection.
13066  * @param {Number} index The index within the collection of the item to remove.
13067  */
13068     removeAt : function(index){
13069         if(index < this.length && index >= 0){
13070             this.length--;
13071             var o = this.items[index];
13072             this.items.splice(index, 1);
13073             var key = this.keys[index];
13074             if(typeof key != "undefined"){
13075                 delete this.map[key];
13076             }
13077             this.keys.splice(index, 1);
13078             this.fireEvent("remove", o, key);
13079         }
13080     },
13081    
13082 /**
13083  * Removed an item associated with the passed key fom the collection.
13084  * @param {String} key The key of the item to remove.
13085  */
13086     removeKey : function(key){
13087         return this.removeAt(this.indexOfKey(key));
13088     },
13089    
13090 /**
13091  * Returns the number of items in the collection.
13092  * @return {Number} the number of items in the collection.
13093  */
13094     getCount : function(){
13095         return this.length; 
13096     },
13097    
13098 /**
13099  * Returns index within the collection of the passed Object.
13100  * @param {Object} o The item to find the index of.
13101  * @return {Number} index of the item.
13102  */
13103     indexOf : function(o){
13104         if(!this.items.indexOf){
13105             for(var i = 0, len = this.items.length; i < len; i++){
13106                 if(this.items[i] == o) {
13107                     return i;
13108                 }
13109             }
13110             return -1;
13111         }else{
13112             return this.items.indexOf(o);
13113         }
13114     },
13115    
13116 /**
13117  * Returns index within the collection of the passed key.
13118  * @param {String} key The key to find the index of.
13119  * @return {Number} index of the key.
13120  */
13121     indexOfKey : function(key){
13122         if(!this.keys.indexOf){
13123             for(var i = 0, len = this.keys.length; i < len; i++){
13124                 if(this.keys[i] == key) {
13125                     return i;
13126                 }
13127             }
13128             return -1;
13129         }else{
13130             return this.keys.indexOf(key);
13131         }
13132     },
13133    
13134 /**
13135  * Returns the item associated with the passed key OR index. Key has priority over index.
13136  * @param {String/Number} key The key or index of the item.
13137  * @return {Object} The item associated with the passed key.
13138  */
13139     item : function(key){
13140         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13141         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13142     },
13143     
13144 /**
13145  * Returns the item at the specified index.
13146  * @param {Number} index The index of the item.
13147  * @return {Object}
13148  */
13149     itemAt : function(index){
13150         return this.items[index];
13151     },
13152     
13153 /**
13154  * Returns the item associated with the passed key.
13155  * @param {String/Number} key The key of the item.
13156  * @return {Object} The item associated with the passed key.
13157  */
13158     key : function(key){
13159         return this.map[key];
13160     },
13161    
13162 /**
13163  * Returns true if the collection contains the passed Object as an item.
13164  * @param {Object} o  The Object to look for in the collection.
13165  * @return {Boolean} True if the collection contains the Object as an item.
13166  */
13167     contains : function(o){
13168         return this.indexOf(o) != -1;
13169     },
13170    
13171 /**
13172  * Returns true if the collection contains the passed Object as a key.
13173  * @param {String} key The key to look for in the collection.
13174  * @return {Boolean} True if the collection contains the Object as a key.
13175  */
13176     containsKey : function(key){
13177         return typeof this.map[key] != "undefined";
13178     },
13179    
13180 /**
13181  * Removes all items from the collection.
13182  */
13183     clear : function(){
13184         this.length = 0;
13185         this.items = [];
13186         this.keys = [];
13187         this.map = {};
13188         this.fireEvent("clear");
13189     },
13190    
13191 /**
13192  * Returns the first item in the collection.
13193  * @return {Object} the first item in the collection..
13194  */
13195     first : function(){
13196         return this.items[0]; 
13197     },
13198    
13199 /**
13200  * Returns the last item in the collection.
13201  * @return {Object} the last item in the collection..
13202  */
13203     last : function(){
13204         return this.items[this.length-1];   
13205     },
13206     
13207     _sort : function(property, dir, fn){
13208         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13209         fn = fn || function(a, b){
13210             return a-b;
13211         };
13212         var c = [], k = this.keys, items = this.items;
13213         for(var i = 0, len = items.length; i < len; i++){
13214             c[c.length] = {key: k[i], value: items[i], index: i};
13215         }
13216         c.sort(function(a, b){
13217             var v = fn(a[property], b[property]) * dsc;
13218             if(v == 0){
13219                 v = (a.index < b.index ? -1 : 1);
13220             }
13221             return v;
13222         });
13223         for(var i = 0, len = c.length; i < len; i++){
13224             items[i] = c[i].value;
13225             k[i] = c[i].key;
13226         }
13227         this.fireEvent("sort", this);
13228     },
13229     
13230     /**
13231      * Sorts this collection with the passed comparison function
13232      * @param {String} direction (optional) "ASC" or "DESC"
13233      * @param {Function} fn (optional) comparison function
13234      */
13235     sort : function(dir, fn){
13236         this._sort("value", dir, fn);
13237     },
13238     
13239     /**
13240      * Sorts this collection by keys
13241      * @param {String} direction (optional) "ASC" or "DESC"
13242      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13243      */
13244     keySort : function(dir, fn){
13245         this._sort("key", dir, fn || function(a, b){
13246             return String(a).toUpperCase()-String(b).toUpperCase();
13247         });
13248     },
13249     
13250     /**
13251      * Returns a range of items in this collection
13252      * @param {Number} startIndex (optional) defaults to 0
13253      * @param {Number} endIndex (optional) default to the last item
13254      * @return {Array} An array of items
13255      */
13256     getRange : function(start, end){
13257         var items = this.items;
13258         if(items.length < 1){
13259             return [];
13260         }
13261         start = start || 0;
13262         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13263         var r = [];
13264         if(start <= end){
13265             for(var i = start; i <= end; i++) {
13266                     r[r.length] = items[i];
13267             }
13268         }else{
13269             for(var i = start; i >= end; i--) {
13270                     r[r.length] = items[i];
13271             }
13272         }
13273         return r;
13274     },
13275         
13276     /**
13277      * Filter the <i>objects</i> in this collection by a specific property. 
13278      * Returns a new collection that has been filtered.
13279      * @param {String} property A property on your objects
13280      * @param {String/RegExp} value Either string that the property values 
13281      * should start with or a RegExp to test against the property
13282      * @return {MixedCollection} The new filtered collection
13283      */
13284     filter : function(property, value){
13285         if(!value.exec){ // not a regex
13286             value = String(value);
13287             if(value.length == 0){
13288                 return this.clone();
13289             }
13290             value = new RegExp("^" + Roo.escapeRe(value), "i");
13291         }
13292         return this.filterBy(function(o){
13293             return o && value.test(o[property]);
13294         });
13295         },
13296     
13297     /**
13298      * Filter by a function. * Returns a new collection that has been filtered.
13299      * The passed function will be called with each 
13300      * object in the collection. If the function returns true, the value is included 
13301      * otherwise it is filtered.
13302      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13303      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13304      * @return {MixedCollection} The new filtered collection
13305      */
13306     filterBy : function(fn, scope){
13307         var r = new Roo.util.MixedCollection();
13308         r.getKey = this.getKey;
13309         var k = this.keys, it = this.items;
13310         for(var i = 0, len = it.length; i < len; i++){
13311             if(fn.call(scope||this, it[i], k[i])){
13312                                 r.add(k[i], it[i]);
13313                         }
13314         }
13315         return r;
13316     },
13317     
13318     /**
13319      * Creates a duplicate of this collection
13320      * @return {MixedCollection}
13321      */
13322     clone : function(){
13323         var r = new Roo.util.MixedCollection();
13324         var k = this.keys, it = this.items;
13325         for(var i = 0, len = it.length; i < len; i++){
13326             r.add(k[i], it[i]);
13327         }
13328         r.getKey = this.getKey;
13329         return r;
13330     }
13331 });
13332 /**
13333  * Returns the item associated with the passed key or index.
13334  * @method
13335  * @param {String/Number} key The key or index of the item.
13336  * @return {Object} The item associated with the passed key.
13337  */
13338 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13339  * Based on:
13340  * Ext JS Library 1.1.1
13341  * Copyright(c) 2006-2007, Ext JS, LLC.
13342  *
13343  * Originally Released Under LGPL - original licence link has changed is not relivant.
13344  *
13345  * Fork - LGPL
13346  * <script type="text/javascript">
13347  */
13348 /**
13349  * @class Roo.util.JSON
13350  * Modified version of Douglas Crockford"s json.js that doesn"t
13351  * mess with the Object prototype 
13352  * http://www.json.org/js.html
13353  * @singleton
13354  */
13355 Roo.util.JSON = new (function(){
13356     var useHasOwn = {}.hasOwnProperty ? true : false;
13357     
13358     // crashes Safari in some instances
13359     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13360     
13361     var pad = function(n) {
13362         return n < 10 ? "0" + n : n;
13363     };
13364     
13365     var m = {
13366         "\b": '\\b',
13367         "\t": '\\t',
13368         "\n": '\\n',
13369         "\f": '\\f',
13370         "\r": '\\r',
13371         '"' : '\\"',
13372         "\\": '\\\\'
13373     };
13374
13375     var encodeString = function(s){
13376         if (/["\\\x00-\x1f]/.test(s)) {
13377             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13378                 var c = m[b];
13379                 if(c){
13380                     return c;
13381                 }
13382                 c = b.charCodeAt();
13383                 return "\\u00" +
13384                     Math.floor(c / 16).toString(16) +
13385                     (c % 16).toString(16);
13386             }) + '"';
13387         }
13388         return '"' + s + '"';
13389     };
13390     
13391     var encodeArray = function(o){
13392         var a = ["["], b, i, l = o.length, v;
13393             for (i = 0; i < l; i += 1) {
13394                 v = o[i];
13395                 switch (typeof v) {
13396                     case "undefined":
13397                     case "function":
13398                     case "unknown":
13399                         break;
13400                     default:
13401                         if (b) {
13402                             a.push(',');
13403                         }
13404                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13405                         b = true;
13406                 }
13407             }
13408             a.push("]");
13409             return a.join("");
13410     };
13411     
13412     var encodeDate = function(o){
13413         return '"' + o.getFullYear() + "-" +
13414                 pad(o.getMonth() + 1) + "-" +
13415                 pad(o.getDate()) + "T" +
13416                 pad(o.getHours()) + ":" +
13417                 pad(o.getMinutes()) + ":" +
13418                 pad(o.getSeconds()) + '"';
13419     };
13420     
13421     /**
13422      * Encodes an Object, Array or other value
13423      * @param {Mixed} o The variable to encode
13424      * @return {String} The JSON string
13425      */
13426     this.encode = function(o)
13427     {
13428         // should this be extended to fully wrap stringify..
13429         
13430         if(typeof o == "undefined" || o === null){
13431             return "null";
13432         }else if(o instanceof Array){
13433             return encodeArray(o);
13434         }else if(o instanceof Date){
13435             return encodeDate(o);
13436         }else if(typeof o == "string"){
13437             return encodeString(o);
13438         }else if(typeof o == "number"){
13439             return isFinite(o) ? String(o) : "null";
13440         }else if(typeof o == "boolean"){
13441             return String(o);
13442         }else {
13443             var a = ["{"], b, i, v;
13444             for (i in o) {
13445                 if(!useHasOwn || o.hasOwnProperty(i)) {
13446                     v = o[i];
13447                     switch (typeof v) {
13448                     case "undefined":
13449                     case "function":
13450                     case "unknown":
13451                         break;
13452                     default:
13453                         if(b){
13454                             a.push(',');
13455                         }
13456                         a.push(this.encode(i), ":",
13457                                 v === null ? "null" : this.encode(v));
13458                         b = true;
13459                     }
13460                 }
13461             }
13462             a.push("}");
13463             return a.join("");
13464         }
13465     };
13466     
13467     /**
13468      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13469      * @param {String} json The JSON string
13470      * @return {Object} The resulting object
13471      */
13472     this.decode = function(json){
13473         
13474         return  /** eval:var:json */ eval("(" + json + ')');
13475     };
13476 })();
13477 /** 
13478  * Shorthand for {@link Roo.util.JSON#encode}
13479  * @member Roo encode 
13480  * @method */
13481 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13482 /** 
13483  * Shorthand for {@link Roo.util.JSON#decode}
13484  * @member Roo decode 
13485  * @method */
13486 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13487 /*
13488  * Based on:
13489  * Ext JS Library 1.1.1
13490  * Copyright(c) 2006-2007, Ext JS, LLC.
13491  *
13492  * Originally Released Under LGPL - original licence link has changed is not relivant.
13493  *
13494  * Fork - LGPL
13495  * <script type="text/javascript">
13496  */
13497  
13498 /**
13499  * @class Roo.util.Format
13500  * Reusable data formatting functions
13501  * @singleton
13502  */
13503 Roo.util.Format = function(){
13504     var trimRe = /^\s+|\s+$/g;
13505     return {
13506         /**
13507          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13508          * @param {String} value The string to truncate
13509          * @param {Number} length The maximum length to allow before truncating
13510          * @return {String} The converted text
13511          */
13512         ellipsis : function(value, len){
13513             if(value && value.length > len){
13514                 return value.substr(0, len-3)+"...";
13515             }
13516             return value;
13517         },
13518
13519         /**
13520          * Checks a reference and converts it to empty string if it is undefined
13521          * @param {Mixed} value Reference to check
13522          * @return {Mixed} Empty string if converted, otherwise the original value
13523          */
13524         undef : function(value){
13525             return typeof value != "undefined" ? value : "";
13526         },
13527
13528         /**
13529          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13530          * @param {String} value The string to encode
13531          * @return {String} The encoded text
13532          */
13533         htmlEncode : function(value){
13534             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13535         },
13536
13537         /**
13538          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13539          * @param {String} value The string to decode
13540          * @return {String} The decoded text
13541          */
13542         htmlDecode : function(value){
13543             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13544         },
13545
13546         /**
13547          * Trims any whitespace from either side of a string
13548          * @param {String} value The text to trim
13549          * @return {String} The trimmed text
13550          */
13551         trim : function(value){
13552             return String(value).replace(trimRe, "");
13553         },
13554
13555         /**
13556          * Returns a substring from within an original string
13557          * @param {String} value The original text
13558          * @param {Number} start The start index of the substring
13559          * @param {Number} length The length of the substring
13560          * @return {String} The substring
13561          */
13562         substr : function(value, start, length){
13563             return String(value).substr(start, length);
13564         },
13565
13566         /**
13567          * Converts a string to all lower case letters
13568          * @param {String} value The text to convert
13569          * @return {String} The converted text
13570          */
13571         lowercase : function(value){
13572             return String(value).toLowerCase();
13573         },
13574
13575         /**
13576          * Converts a string to all upper case letters
13577          * @param {String} value The text to convert
13578          * @return {String} The converted text
13579          */
13580         uppercase : function(value){
13581             return String(value).toUpperCase();
13582         },
13583
13584         /**
13585          * Converts the first character only of a string to upper case
13586          * @param {String} value The text to convert
13587          * @return {String} The converted text
13588          */
13589         capitalize : function(value){
13590             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13591         },
13592
13593         // private
13594         call : function(value, fn){
13595             if(arguments.length > 2){
13596                 var args = Array.prototype.slice.call(arguments, 2);
13597                 args.unshift(value);
13598                  
13599                 return /** eval:var:value */  eval(fn).apply(window, args);
13600             }else{
13601                 /** eval:var:value */
13602                 return /** eval:var:value */ eval(fn).call(window, value);
13603             }
13604         },
13605
13606        
13607         /**
13608          * safer version of Math.toFixed..??/
13609          * @param {Number/String} value The numeric value to format
13610          * @param {Number/String} value Decimal places 
13611          * @return {String} The formatted currency string
13612          */
13613         toFixed : function(v, n)
13614         {
13615             // why not use to fixed - precision is buggered???
13616             if (!n) {
13617                 return Math.round(v-0);
13618             }
13619             var fact = Math.pow(10,n+1);
13620             v = (Math.round((v-0)*fact))/fact;
13621             var z = (''+fact).substring(2);
13622             if (v == Math.floor(v)) {
13623                 return Math.floor(v) + '.' + z;
13624             }
13625             
13626             // now just padd decimals..
13627             var ps = String(v).split('.');
13628             var fd = (ps[1] + z);
13629             var r = fd.substring(0,n); 
13630             var rm = fd.substring(n); 
13631             if (rm < 5) {
13632                 return ps[0] + '.' + r;
13633             }
13634             r*=1; // turn it into a number;
13635             r++;
13636             if (String(r).length != n) {
13637                 ps[0]*=1;
13638                 ps[0]++;
13639                 r = String(r).substring(1); // chop the end off.
13640             }
13641             
13642             return ps[0] + '.' + r;
13643              
13644         },
13645         
13646         /**
13647          * Format a number as US currency
13648          * @param {Number/String} value The numeric value to format
13649          * @return {String} The formatted currency string
13650          */
13651         usMoney : function(v){
13652             return '$' + Roo.util.Format.number(v);
13653         },
13654         
13655         /**
13656          * Format a number
13657          * eventually this should probably emulate php's number_format
13658          * @param {Number/String} value The numeric value to format
13659          * @param {Number} decimals number of decimal places
13660          * @return {String} The formatted currency string
13661          */
13662         number : function(v,decimals)
13663         {
13664             // multiply and round.
13665             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13666             var mul = Math.pow(10, decimals);
13667             var zero = String(mul).substring(1);
13668             v = (Math.round((v-0)*mul))/mul;
13669             
13670             // if it's '0' number.. then
13671             
13672             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13673             v = String(v);
13674             var ps = v.split('.');
13675             var whole = ps[0];
13676             
13677             
13678             var r = /(\d+)(\d{3})/;
13679             // add comma's
13680             while (r.test(whole)) {
13681                 whole = whole.replace(r, '$1' + ',' + '$2');
13682             }
13683             
13684             
13685             var sub = ps[1] ?
13686                     // has decimals..
13687                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13688                     // does not have decimals
13689                     (decimals ? ('.' + zero) : '');
13690             
13691             
13692             return whole + sub ;
13693         },
13694         
13695         /**
13696          * Parse a value into a formatted date using the specified format pattern.
13697          * @param {Mixed} value The value to format
13698          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13699          * @return {String} The formatted date string
13700          */
13701         date : function(v, format){
13702             if(!v){
13703                 return "";
13704             }
13705             if(!(v instanceof Date)){
13706                 v = new Date(Date.parse(v));
13707             }
13708             return v.dateFormat(format || Roo.util.Format.defaults.date);
13709         },
13710
13711         /**
13712          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13713          * @param {String} format Any valid date format string
13714          * @return {Function} The date formatting function
13715          */
13716         dateRenderer : function(format){
13717             return function(v){
13718                 return Roo.util.Format.date(v, format);  
13719             };
13720         },
13721
13722         // private
13723         stripTagsRE : /<\/?[^>]+>/gi,
13724         
13725         /**
13726          * Strips all HTML tags
13727          * @param {Mixed} value The text from which to strip tags
13728          * @return {String} The stripped text
13729          */
13730         stripTags : function(v){
13731             return !v ? v : String(v).replace(this.stripTagsRE, "");
13732         }
13733     };
13734 }();
13735 Roo.util.Format.defaults = {
13736     date : 'd/M/Y'
13737 };/*
13738  * Based on:
13739  * Ext JS Library 1.1.1
13740  * Copyright(c) 2006-2007, Ext JS, LLC.
13741  *
13742  * Originally Released Under LGPL - original licence link has changed is not relivant.
13743  *
13744  * Fork - LGPL
13745  * <script type="text/javascript">
13746  */
13747
13748
13749  
13750
13751 /**
13752  * @class Roo.MasterTemplate
13753  * @extends Roo.Template
13754  * Provides a template that can have child templates. The syntax is:
13755 <pre><code>
13756 var t = new Roo.MasterTemplate(
13757         '&lt;select name="{name}"&gt;',
13758                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13759         '&lt;/select&gt;'
13760 );
13761 t.add('options', {value: 'foo', text: 'bar'});
13762 // or you can add multiple child elements in one shot
13763 t.addAll('options', [
13764     {value: 'foo', text: 'bar'},
13765     {value: 'foo2', text: 'bar2'},
13766     {value: 'foo3', text: 'bar3'}
13767 ]);
13768 // then append, applying the master template values
13769 t.append('my-form', {name: 'my-select'});
13770 </code></pre>
13771 * A name attribute for the child template is not required if you have only one child
13772 * template or you want to refer to them by index.
13773  */
13774 Roo.MasterTemplate = function(){
13775     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13776     this.originalHtml = this.html;
13777     var st = {};
13778     var m, re = this.subTemplateRe;
13779     re.lastIndex = 0;
13780     var subIndex = 0;
13781     while(m = re.exec(this.html)){
13782         var name = m[1], content = m[2];
13783         st[subIndex] = {
13784             name: name,
13785             index: subIndex,
13786             buffer: [],
13787             tpl : new Roo.Template(content)
13788         };
13789         if(name){
13790             st[name] = st[subIndex];
13791         }
13792         st[subIndex].tpl.compile();
13793         st[subIndex].tpl.call = this.call.createDelegate(this);
13794         subIndex++;
13795     }
13796     this.subCount = subIndex;
13797     this.subs = st;
13798 };
13799 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13800     /**
13801     * The regular expression used to match sub templates
13802     * @type RegExp
13803     * @property
13804     */
13805     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13806
13807     /**
13808      * Applies the passed values to a child template.
13809      * @param {String/Number} name (optional) The name or index of the child template
13810      * @param {Array/Object} values The values to be applied to the template
13811      * @return {MasterTemplate} this
13812      */
13813      add : function(name, values){
13814         if(arguments.length == 1){
13815             values = arguments[0];
13816             name = 0;
13817         }
13818         var s = this.subs[name];
13819         s.buffer[s.buffer.length] = s.tpl.apply(values);
13820         return this;
13821     },
13822
13823     /**
13824      * Applies all the passed values to a child template.
13825      * @param {String/Number} name (optional) The name or index of the child template
13826      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13827      * @param {Boolean} reset (optional) True to reset the template first
13828      * @return {MasterTemplate} this
13829      */
13830     fill : function(name, values, reset){
13831         var a = arguments;
13832         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13833             values = a[0];
13834             name = 0;
13835             reset = a[1];
13836         }
13837         if(reset){
13838             this.reset();
13839         }
13840         for(var i = 0, len = values.length; i < len; i++){
13841             this.add(name, values[i]);
13842         }
13843         return this;
13844     },
13845
13846     /**
13847      * Resets the template for reuse
13848      * @return {MasterTemplate} this
13849      */
13850      reset : function(){
13851         var s = this.subs;
13852         for(var i = 0; i < this.subCount; i++){
13853             s[i].buffer = [];
13854         }
13855         return this;
13856     },
13857
13858     applyTemplate : function(values){
13859         var s = this.subs;
13860         var replaceIndex = -1;
13861         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13862             return s[++replaceIndex].buffer.join("");
13863         });
13864         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13865     },
13866
13867     apply : function(){
13868         return this.applyTemplate.apply(this, arguments);
13869     },
13870
13871     compile : function(){return this;}
13872 });
13873
13874 /**
13875  * Alias for fill().
13876  * @method
13877  */
13878 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13879  /**
13880  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13881  * var tpl = Roo.MasterTemplate.from('element-id');
13882  * @param {String/HTMLElement} el
13883  * @param {Object} config
13884  * @static
13885  */
13886 Roo.MasterTemplate.from = function(el, config){
13887     el = Roo.getDom(el);
13888     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13889 };/*
13890  * Based on:
13891  * Ext JS Library 1.1.1
13892  * Copyright(c) 2006-2007, Ext JS, LLC.
13893  *
13894  * Originally Released Under LGPL - original licence link has changed is not relivant.
13895  *
13896  * Fork - LGPL
13897  * <script type="text/javascript">
13898  */
13899
13900  
13901 /**
13902  * @class Roo.util.CSS
13903  * Utility class for manipulating CSS rules
13904  * @singleton
13905  */
13906 Roo.util.CSS = function(){
13907         var rules = null;
13908         var doc = document;
13909
13910     var camelRe = /(-[a-z])/gi;
13911     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13912
13913    return {
13914    /**
13915     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13916     * tag and appended to the HEAD of the document.
13917     * @param {String|Object} cssText The text containing the css rules
13918     * @param {String} id An id to add to the stylesheet for later removal
13919     * @return {StyleSheet}
13920     */
13921     createStyleSheet : function(cssText, id){
13922         var ss;
13923         var head = doc.getElementsByTagName("head")[0];
13924         var nrules = doc.createElement("style");
13925         nrules.setAttribute("type", "text/css");
13926         if(id){
13927             nrules.setAttribute("id", id);
13928         }
13929         if (typeof(cssText) != 'string') {
13930             // support object maps..
13931             // not sure if this a good idea.. 
13932             // perhaps it should be merged with the general css handling
13933             // and handle js style props.
13934             var cssTextNew = [];
13935             for(var n in cssText) {
13936                 var citems = [];
13937                 for(var k in cssText[n]) {
13938                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13939                 }
13940                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13941                 
13942             }
13943             cssText = cssTextNew.join("\n");
13944             
13945         }
13946        
13947        
13948        if(Roo.isIE){
13949            head.appendChild(nrules);
13950            ss = nrules.styleSheet;
13951            ss.cssText = cssText;
13952        }else{
13953            try{
13954                 nrules.appendChild(doc.createTextNode(cssText));
13955            }catch(e){
13956                nrules.cssText = cssText; 
13957            }
13958            head.appendChild(nrules);
13959            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13960        }
13961        this.cacheStyleSheet(ss);
13962        return ss;
13963    },
13964
13965    /**
13966     * Removes a style or link tag by id
13967     * @param {String} id The id of the tag
13968     */
13969    removeStyleSheet : function(id){
13970        var existing = doc.getElementById(id);
13971        if(existing){
13972            existing.parentNode.removeChild(existing);
13973        }
13974    },
13975
13976    /**
13977     * Dynamically swaps an existing stylesheet reference for a new one
13978     * @param {String} id The id of an existing link tag to remove
13979     * @param {String} url The href of the new stylesheet to include
13980     */
13981    swapStyleSheet : function(id, url){
13982        this.removeStyleSheet(id);
13983        var ss = doc.createElement("link");
13984        ss.setAttribute("rel", "stylesheet");
13985        ss.setAttribute("type", "text/css");
13986        ss.setAttribute("id", id);
13987        ss.setAttribute("href", url);
13988        doc.getElementsByTagName("head")[0].appendChild(ss);
13989    },
13990    
13991    /**
13992     * Refresh the rule cache if you have dynamically added stylesheets
13993     * @return {Object} An object (hash) of rules indexed by selector
13994     */
13995    refreshCache : function(){
13996        return this.getRules(true);
13997    },
13998
13999    // private
14000    cacheStyleSheet : function(stylesheet){
14001        if(!rules){
14002            rules = {};
14003        }
14004        try{// try catch for cross domain access issue
14005            var ssRules = stylesheet.cssRules || stylesheet.rules;
14006            for(var j = ssRules.length-1; j >= 0; --j){
14007                rules[ssRules[j].selectorText] = ssRules[j];
14008            }
14009        }catch(e){}
14010    },
14011    
14012    /**
14013     * Gets all css rules for the document
14014     * @param {Boolean} refreshCache true to refresh the internal cache
14015     * @return {Object} An object (hash) of rules indexed by selector
14016     */
14017    getRules : function(refreshCache){
14018                 if(rules == null || refreshCache){
14019                         rules = {};
14020                         var ds = doc.styleSheets;
14021                         for(var i =0, len = ds.length; i < len; i++){
14022                             try{
14023                         this.cacheStyleSheet(ds[i]);
14024                     }catch(e){} 
14025                 }
14026                 }
14027                 return rules;
14028         },
14029         
14030         /**
14031     * Gets an an individual CSS rule by selector(s)
14032     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14033     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14034     * @return {CSSRule} The CSS rule or null if one is not found
14035     */
14036    getRule : function(selector, refreshCache){
14037                 var rs = this.getRules(refreshCache);
14038                 if(!(selector instanceof Array)){
14039                     return rs[selector];
14040                 }
14041                 for(var i = 0; i < selector.length; i++){
14042                         if(rs[selector[i]]){
14043                                 return rs[selector[i]];
14044                         }
14045                 }
14046                 return null;
14047         },
14048         
14049         
14050         /**
14051     * Updates a rule property
14052     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14053     * @param {String} property The css property
14054     * @param {String} value The new value for the property
14055     * @return {Boolean} true If a rule was found and updated
14056     */
14057    updateRule : function(selector, property, value){
14058                 if(!(selector instanceof Array)){
14059                         var rule = this.getRule(selector);
14060                         if(rule){
14061                                 rule.style[property.replace(camelRe, camelFn)] = value;
14062                                 return true;
14063                         }
14064                 }else{
14065                         for(var i = 0; i < selector.length; i++){
14066                                 if(this.updateRule(selector[i], property, value)){
14067                                         return true;
14068                                 }
14069                         }
14070                 }
14071                 return false;
14072         }
14073    };   
14074 }();/*
14075  * Based on:
14076  * Ext JS Library 1.1.1
14077  * Copyright(c) 2006-2007, Ext JS, LLC.
14078  *
14079  * Originally Released Under LGPL - original licence link has changed is not relivant.
14080  *
14081  * Fork - LGPL
14082  * <script type="text/javascript">
14083  */
14084
14085  
14086
14087 /**
14088  * @class Roo.util.ClickRepeater
14089  * @extends Roo.util.Observable
14090  * 
14091  * A wrapper class which can be applied to any element. Fires a "click" event while the
14092  * mouse is pressed. The interval between firings may be specified in the config but
14093  * defaults to 10 milliseconds.
14094  * 
14095  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14096  * 
14097  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14098  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14099  * Similar to an autorepeat key delay.
14100  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14101  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14102  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14103  *           "interval" and "delay" are ignored. "immediate" is honored.
14104  * @cfg {Boolean} preventDefault True to prevent the default click event
14105  * @cfg {Boolean} stopDefault True to stop the default click event
14106  * 
14107  * @history
14108  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14109  *     2007-02-02 jvs Renamed to ClickRepeater
14110  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14111  *
14112  *  @constructor
14113  * @param {String/HTMLElement/Element} el The element to listen on
14114  * @param {Object} config
14115  **/
14116 Roo.util.ClickRepeater = function(el, config)
14117 {
14118     this.el = Roo.get(el);
14119     this.el.unselectable();
14120
14121     Roo.apply(this, config);
14122
14123     this.addEvents({
14124     /**
14125      * @event mousedown
14126      * Fires when the mouse button is depressed.
14127      * @param {Roo.util.ClickRepeater} this
14128      */
14129         "mousedown" : true,
14130     /**
14131      * @event click
14132      * Fires on a specified interval during the time the element is pressed.
14133      * @param {Roo.util.ClickRepeater} this
14134      */
14135         "click" : true,
14136     /**
14137      * @event mouseup
14138      * Fires when the mouse key is released.
14139      * @param {Roo.util.ClickRepeater} this
14140      */
14141         "mouseup" : true
14142     });
14143
14144     this.el.on("mousedown", this.handleMouseDown, this);
14145     if(this.preventDefault || this.stopDefault){
14146         this.el.on("click", function(e){
14147             if(this.preventDefault){
14148                 e.preventDefault();
14149             }
14150             if(this.stopDefault){
14151                 e.stopEvent();
14152             }
14153         }, this);
14154     }
14155
14156     // allow inline handler
14157     if(this.handler){
14158         this.on("click", this.handler,  this.scope || this);
14159     }
14160
14161     Roo.util.ClickRepeater.superclass.constructor.call(this);
14162 };
14163
14164 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14165     interval : 20,
14166     delay: 250,
14167     preventDefault : true,
14168     stopDefault : false,
14169     timer : 0,
14170
14171     // private
14172     handleMouseDown : function(){
14173         clearTimeout(this.timer);
14174         this.el.blur();
14175         if(this.pressClass){
14176             this.el.addClass(this.pressClass);
14177         }
14178         this.mousedownTime = new Date();
14179
14180         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14181         this.el.on("mouseout", this.handleMouseOut, this);
14182
14183         this.fireEvent("mousedown", this);
14184         this.fireEvent("click", this);
14185         
14186         this.timer = this.click.defer(this.delay || this.interval, this);
14187     },
14188
14189     // private
14190     click : function(){
14191         this.fireEvent("click", this);
14192         this.timer = this.click.defer(this.getInterval(), this);
14193     },
14194
14195     // private
14196     getInterval: function(){
14197         if(!this.accelerate){
14198             return this.interval;
14199         }
14200         var pressTime = this.mousedownTime.getElapsed();
14201         if(pressTime < 500){
14202             return 400;
14203         }else if(pressTime < 1700){
14204             return 320;
14205         }else if(pressTime < 2600){
14206             return 250;
14207         }else if(pressTime < 3500){
14208             return 180;
14209         }else if(pressTime < 4400){
14210             return 140;
14211         }else if(pressTime < 5300){
14212             return 80;
14213         }else if(pressTime < 6200){
14214             return 50;
14215         }else{
14216             return 10;
14217         }
14218     },
14219
14220     // private
14221     handleMouseOut : function(){
14222         clearTimeout(this.timer);
14223         if(this.pressClass){
14224             this.el.removeClass(this.pressClass);
14225         }
14226         this.el.on("mouseover", this.handleMouseReturn, this);
14227     },
14228
14229     // private
14230     handleMouseReturn : function(){
14231         this.el.un("mouseover", this.handleMouseReturn);
14232         if(this.pressClass){
14233             this.el.addClass(this.pressClass);
14234         }
14235         this.click();
14236     },
14237
14238     // private
14239     handleMouseUp : function(){
14240         clearTimeout(this.timer);
14241         this.el.un("mouseover", this.handleMouseReturn);
14242         this.el.un("mouseout", this.handleMouseOut);
14243         Roo.get(document).un("mouseup", this.handleMouseUp);
14244         this.el.removeClass(this.pressClass);
14245         this.fireEvent("mouseup", this);
14246     }
14247 });/*
14248  * Based on:
14249  * Ext JS Library 1.1.1
14250  * Copyright(c) 2006-2007, Ext JS, LLC.
14251  *
14252  * Originally Released Under LGPL - original licence link has changed is not relivant.
14253  *
14254  * Fork - LGPL
14255  * <script type="text/javascript">
14256  */
14257
14258  
14259 /**
14260  * @class Roo.KeyNav
14261  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14262  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14263  * way to implement custom navigation schemes for any UI component.</p>
14264  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14265  * pageUp, pageDown, del, home, end.  Usage:</p>
14266  <pre><code>
14267 var nav = new Roo.KeyNav("my-element", {
14268     "left" : function(e){
14269         this.moveLeft(e.ctrlKey);
14270     },
14271     "right" : function(e){
14272         this.moveRight(e.ctrlKey);
14273     },
14274     "enter" : function(e){
14275         this.save();
14276     },
14277     scope : this
14278 });
14279 </code></pre>
14280  * @constructor
14281  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14282  * @param {Object} config The config
14283  */
14284 Roo.KeyNav = function(el, config){
14285     this.el = Roo.get(el);
14286     Roo.apply(this, config);
14287     if(!this.disabled){
14288         this.disabled = true;
14289         this.enable();
14290     }
14291 };
14292
14293 Roo.KeyNav.prototype = {
14294     /**
14295      * @cfg {Boolean} disabled
14296      * True to disable this KeyNav instance (defaults to false)
14297      */
14298     disabled : false,
14299     /**
14300      * @cfg {String} defaultEventAction
14301      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14302      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14303      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14304      */
14305     defaultEventAction: "stopEvent",
14306     /**
14307      * @cfg {Boolean} forceKeyDown
14308      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14309      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14310      * handle keydown instead of keypress.
14311      */
14312     forceKeyDown : false,
14313
14314     // private
14315     prepareEvent : function(e){
14316         var k = e.getKey();
14317         var h = this.keyToHandler[k];
14318         //if(h && this[h]){
14319         //    e.stopPropagation();
14320         //}
14321         if(Roo.isSafari && h && k >= 37 && k <= 40){
14322             e.stopEvent();
14323         }
14324     },
14325
14326     // private
14327     relay : function(e){
14328         var k = e.getKey();
14329         var h = this.keyToHandler[k];
14330         if(h && this[h]){
14331             if(this.doRelay(e, this[h], h) !== true){
14332                 e[this.defaultEventAction]();
14333             }
14334         }
14335     },
14336
14337     // private
14338     doRelay : function(e, h, hname){
14339         return h.call(this.scope || this, e);
14340     },
14341
14342     // possible handlers
14343     enter : false,
14344     left : false,
14345     right : false,
14346     up : false,
14347     down : false,
14348     tab : false,
14349     esc : false,
14350     pageUp : false,
14351     pageDown : false,
14352     del : false,
14353     home : false,
14354     end : false,
14355
14356     // quick lookup hash
14357     keyToHandler : {
14358         37 : "left",
14359         39 : "right",
14360         38 : "up",
14361         40 : "down",
14362         33 : "pageUp",
14363         34 : "pageDown",
14364         46 : "del",
14365         36 : "home",
14366         35 : "end",
14367         13 : "enter",
14368         27 : "esc",
14369         9  : "tab"
14370     },
14371
14372         /**
14373          * Enable this KeyNav
14374          */
14375         enable: function(){
14376                 if(this.disabled){
14377             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14378             // the EventObject will normalize Safari automatically
14379             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14380                 this.el.on("keydown", this.relay,  this);
14381             }else{
14382                 this.el.on("keydown", this.prepareEvent,  this);
14383                 this.el.on("keypress", this.relay,  this);
14384             }
14385                     this.disabled = false;
14386                 }
14387         },
14388
14389         /**
14390          * Disable this KeyNav
14391          */
14392         disable: function(){
14393                 if(!this.disabled){
14394                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14395                 this.el.un("keydown", this.relay);
14396             }else{
14397                 this.el.un("keydown", this.prepareEvent);
14398                 this.el.un("keypress", this.relay);
14399             }
14400                     this.disabled = true;
14401                 }
14402         }
14403 };/*
14404  * Based on:
14405  * Ext JS Library 1.1.1
14406  * Copyright(c) 2006-2007, Ext JS, LLC.
14407  *
14408  * Originally Released Under LGPL - original licence link has changed is not relivant.
14409  *
14410  * Fork - LGPL
14411  * <script type="text/javascript">
14412  */
14413
14414  
14415 /**
14416  * @class Roo.KeyMap
14417  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14418  * The constructor accepts the same config object as defined by {@link #addBinding}.
14419  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14420  * combination it will call the function with this signature (if the match is a multi-key
14421  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14422  * A KeyMap can also handle a string representation of keys.<br />
14423  * Usage:
14424  <pre><code>
14425 // map one key by key code
14426 var map = new Roo.KeyMap("my-element", {
14427     key: 13, // or Roo.EventObject.ENTER
14428     fn: myHandler,
14429     scope: myObject
14430 });
14431
14432 // map multiple keys to one action by string
14433 var map = new Roo.KeyMap("my-element", {
14434     key: "a\r\n\t",
14435     fn: myHandler,
14436     scope: myObject
14437 });
14438
14439 // map multiple keys to multiple actions by strings and array of codes
14440 var map = new Roo.KeyMap("my-element", [
14441     {
14442         key: [10,13],
14443         fn: function(){ alert("Return was pressed"); }
14444     }, {
14445         key: "abc",
14446         fn: function(){ alert('a, b or c was pressed'); }
14447     }, {
14448         key: "\t",
14449         ctrl:true,
14450         shift:true,
14451         fn: function(){ alert('Control + shift + tab was pressed.'); }
14452     }
14453 ]);
14454 </code></pre>
14455  * <b>Note: A KeyMap starts enabled</b>
14456  * @constructor
14457  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14458  * @param {Object} config The config (see {@link #addBinding})
14459  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14460  */
14461 Roo.KeyMap = function(el, config, eventName){
14462     this.el  = Roo.get(el);
14463     this.eventName = eventName || "keydown";
14464     this.bindings = [];
14465     if(config){
14466         this.addBinding(config);
14467     }
14468     this.enable();
14469 };
14470
14471 Roo.KeyMap.prototype = {
14472     /**
14473      * True to stop the event from bubbling and prevent the default browser action if the
14474      * key was handled by the KeyMap (defaults to false)
14475      * @type Boolean
14476      */
14477     stopEvent : false,
14478
14479     /**
14480      * Add a new binding to this KeyMap. The following config object properties are supported:
14481      * <pre>
14482 Property    Type             Description
14483 ----------  ---------------  ----------------------------------------------------------------------
14484 key         String/Array     A single keycode or an array of keycodes to handle
14485 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14486 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14487 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14488 fn          Function         The function to call when KeyMap finds the expected key combination
14489 scope       Object           The scope of the callback function
14490 </pre>
14491      *
14492      * Usage:
14493      * <pre><code>
14494 // Create a KeyMap
14495 var map = new Roo.KeyMap(document, {
14496     key: Roo.EventObject.ENTER,
14497     fn: handleKey,
14498     scope: this
14499 });
14500
14501 //Add a new binding to the existing KeyMap later
14502 map.addBinding({
14503     key: 'abc',
14504     shift: true,
14505     fn: handleKey,
14506     scope: this
14507 });
14508 </code></pre>
14509      * @param {Object/Array} config A single KeyMap config or an array of configs
14510      */
14511         addBinding : function(config){
14512         if(config instanceof Array){
14513             for(var i = 0, len = config.length; i < len; i++){
14514                 this.addBinding(config[i]);
14515             }
14516             return;
14517         }
14518         var keyCode = config.key,
14519             shift = config.shift, 
14520             ctrl = config.ctrl, 
14521             alt = config.alt,
14522             fn = config.fn,
14523             scope = config.scope;
14524         if(typeof keyCode == "string"){
14525             var ks = [];
14526             var keyString = keyCode.toUpperCase();
14527             for(var j = 0, len = keyString.length; j < len; j++){
14528                 ks.push(keyString.charCodeAt(j));
14529             }
14530             keyCode = ks;
14531         }
14532         var keyArray = keyCode instanceof Array;
14533         var handler = function(e){
14534             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14535                 var k = e.getKey();
14536                 if(keyArray){
14537                     for(var i = 0, len = keyCode.length; i < len; i++){
14538                         if(keyCode[i] == k){
14539                           if(this.stopEvent){
14540                               e.stopEvent();
14541                           }
14542                           fn.call(scope || window, k, e);
14543                           return;
14544                         }
14545                     }
14546                 }else{
14547                     if(k == keyCode){
14548                         if(this.stopEvent){
14549                            e.stopEvent();
14550                         }
14551                         fn.call(scope || window, k, e);
14552                     }
14553                 }
14554             }
14555         };
14556         this.bindings.push(handler);  
14557         },
14558
14559     /**
14560      * Shorthand for adding a single key listener
14561      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14562      * following options:
14563      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14564      * @param {Function} fn The function to call
14565      * @param {Object} scope (optional) The scope of the function
14566      */
14567     on : function(key, fn, scope){
14568         var keyCode, shift, ctrl, alt;
14569         if(typeof key == "object" && !(key instanceof Array)){
14570             keyCode = key.key;
14571             shift = key.shift;
14572             ctrl = key.ctrl;
14573             alt = key.alt;
14574         }else{
14575             keyCode = key;
14576         }
14577         this.addBinding({
14578             key: keyCode,
14579             shift: shift,
14580             ctrl: ctrl,
14581             alt: alt,
14582             fn: fn,
14583             scope: scope
14584         })
14585     },
14586
14587     // private
14588     handleKeyDown : function(e){
14589             if(this.enabled){ //just in case
14590             var b = this.bindings;
14591             for(var i = 0, len = b.length; i < len; i++){
14592                 b[i].call(this, e);
14593             }
14594             }
14595         },
14596         
14597         /**
14598          * Returns true if this KeyMap is enabled
14599          * @return {Boolean} 
14600          */
14601         isEnabled : function(){
14602             return this.enabled;  
14603         },
14604         
14605         /**
14606          * Enables this KeyMap
14607          */
14608         enable: function(){
14609                 if(!this.enabled){
14610                     this.el.on(this.eventName, this.handleKeyDown, this);
14611                     this.enabled = true;
14612                 }
14613         },
14614
14615         /**
14616          * Disable this KeyMap
14617          */
14618         disable: function(){
14619                 if(this.enabled){
14620                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14621                     this.enabled = false;
14622                 }
14623         }
14624 };/*
14625  * Based on:
14626  * Ext JS Library 1.1.1
14627  * Copyright(c) 2006-2007, Ext JS, LLC.
14628  *
14629  * Originally Released Under LGPL - original licence link has changed is not relivant.
14630  *
14631  * Fork - LGPL
14632  * <script type="text/javascript">
14633  */
14634
14635  
14636 /**
14637  * @class Roo.util.TextMetrics
14638  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14639  * wide, in pixels, a given block of text will be.
14640  * @singleton
14641  */
14642 Roo.util.TextMetrics = function(){
14643     var shared;
14644     return {
14645         /**
14646          * Measures the size of the specified text
14647          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14648          * that can affect the size of the rendered text
14649          * @param {String} text The text to measure
14650          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14651          * in order to accurately measure the text height
14652          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14653          */
14654         measure : function(el, text, fixedWidth){
14655             if(!shared){
14656                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14657             }
14658             shared.bind(el);
14659             shared.setFixedWidth(fixedWidth || 'auto');
14660             return shared.getSize(text);
14661         },
14662
14663         /**
14664          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14665          * the overhead of multiple calls to initialize the style properties on each measurement.
14666          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14667          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14668          * in order to accurately measure the text height
14669          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14670          */
14671         createInstance : function(el, fixedWidth){
14672             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14673         }
14674     };
14675 }();
14676
14677  
14678
14679 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14680     var ml = new Roo.Element(document.createElement('div'));
14681     document.body.appendChild(ml.dom);
14682     ml.position('absolute');
14683     ml.setLeftTop(-1000, -1000);
14684     ml.hide();
14685
14686     if(fixedWidth){
14687         ml.setWidth(fixedWidth);
14688     }
14689      
14690     var instance = {
14691         /**
14692          * Returns the size of the specified text based on the internal element's style and width properties
14693          * @memberOf Roo.util.TextMetrics.Instance#
14694          * @param {String} text The text to measure
14695          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14696          */
14697         getSize : function(text){
14698             ml.update(text);
14699             var s = ml.getSize();
14700             ml.update('');
14701             return s;
14702         },
14703
14704         /**
14705          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14706          * that can affect the size of the rendered text
14707          * @memberOf Roo.util.TextMetrics.Instance#
14708          * @param {String/HTMLElement} el The element, dom node or id
14709          */
14710         bind : function(el){
14711             ml.setStyle(
14712                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14713             );
14714         },
14715
14716         /**
14717          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14718          * to set a fixed width in order to accurately measure the text height.
14719          * @memberOf Roo.util.TextMetrics.Instance#
14720          * @param {Number} width The width to set on the element
14721          */
14722         setFixedWidth : function(width){
14723             ml.setWidth(width);
14724         },
14725
14726         /**
14727          * Returns the measured width of the specified text
14728          * @memberOf Roo.util.TextMetrics.Instance#
14729          * @param {String} text The text to measure
14730          * @return {Number} width The width in pixels
14731          */
14732         getWidth : function(text){
14733             ml.dom.style.width = 'auto';
14734             return this.getSize(text).width;
14735         },
14736
14737         /**
14738          * Returns the measured height of the specified text.  For multiline text, be sure to call
14739          * {@link #setFixedWidth} if necessary.
14740          * @memberOf Roo.util.TextMetrics.Instance#
14741          * @param {String} text The text to measure
14742          * @return {Number} height The height in pixels
14743          */
14744         getHeight : function(text){
14745             return this.getSize(text).height;
14746         }
14747     };
14748
14749     instance.bind(bindTo);
14750
14751     return instance;
14752 };
14753
14754 // backwards compat
14755 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14756  * Based on:
14757  * Ext JS Library 1.1.1
14758  * Copyright(c) 2006-2007, Ext JS, LLC.
14759  *
14760  * Originally Released Under LGPL - original licence link has changed is not relivant.
14761  *
14762  * Fork - LGPL
14763  * <script type="text/javascript">
14764  */
14765
14766 /**
14767  * @class Roo.state.Provider
14768  * Abstract base class for state provider implementations. This class provides methods
14769  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14770  * Provider interface.
14771  */
14772 Roo.state.Provider = function(){
14773     /**
14774      * @event statechange
14775      * Fires when a state change occurs.
14776      * @param {Provider} this This state provider
14777      * @param {String} key The state key which was changed
14778      * @param {String} value The encoded value for the state
14779      */
14780     this.addEvents({
14781         "statechange": true
14782     });
14783     this.state = {};
14784     Roo.state.Provider.superclass.constructor.call(this);
14785 };
14786 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14787     /**
14788      * Returns the current value for a key
14789      * @param {String} name The key name
14790      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14791      * @return {Mixed} The state data
14792      */
14793     get : function(name, defaultValue){
14794         return typeof this.state[name] == "undefined" ?
14795             defaultValue : this.state[name];
14796     },
14797     
14798     /**
14799      * Clears a value from the state
14800      * @param {String} name The key name
14801      */
14802     clear : function(name){
14803         delete this.state[name];
14804         this.fireEvent("statechange", this, name, null);
14805     },
14806     
14807     /**
14808      * Sets the value for a key
14809      * @param {String} name The key name
14810      * @param {Mixed} value The value to set
14811      */
14812     set : function(name, value){
14813         this.state[name] = value;
14814         this.fireEvent("statechange", this, name, value);
14815     },
14816     
14817     /**
14818      * Decodes a string previously encoded with {@link #encodeValue}.
14819      * @param {String} value The value to decode
14820      * @return {Mixed} The decoded value
14821      */
14822     decodeValue : function(cookie){
14823         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14824         var matches = re.exec(unescape(cookie));
14825         if(!matches || !matches[1]) {
14826             return; // non state cookie
14827         }
14828         var type = matches[1];
14829         var v = matches[2];
14830         switch(type){
14831             case "n":
14832                 return parseFloat(v);
14833             case "d":
14834                 return new Date(Date.parse(v));
14835             case "b":
14836                 return (v == "1");
14837             case "a":
14838                 var all = [];
14839                 var values = v.split("^");
14840                 for(var i = 0, len = values.length; i < len; i++){
14841                     all.push(this.decodeValue(values[i]));
14842                 }
14843                 return all;
14844            case "o":
14845                 var all = {};
14846                 var values = v.split("^");
14847                 for(var i = 0, len = values.length; i < len; i++){
14848                     var kv = values[i].split("=");
14849                     all[kv[0]] = this.decodeValue(kv[1]);
14850                 }
14851                 return all;
14852            default:
14853                 return v;
14854         }
14855     },
14856     
14857     /**
14858      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14859      * @param {Mixed} value The value to encode
14860      * @return {String} The encoded value
14861      */
14862     encodeValue : function(v){
14863         var enc;
14864         if(typeof v == "number"){
14865             enc = "n:" + v;
14866         }else if(typeof v == "boolean"){
14867             enc = "b:" + (v ? "1" : "0");
14868         }else if(v instanceof Date){
14869             enc = "d:" + v.toGMTString();
14870         }else if(v instanceof Array){
14871             var flat = "";
14872             for(var i = 0, len = v.length; i < len; i++){
14873                 flat += this.encodeValue(v[i]);
14874                 if(i != len-1) {
14875                     flat += "^";
14876                 }
14877             }
14878             enc = "a:" + flat;
14879         }else if(typeof v == "object"){
14880             var flat = "";
14881             for(var key in v){
14882                 if(typeof v[key] != "function"){
14883                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14884                 }
14885             }
14886             enc = "o:" + flat.substring(0, flat.length-1);
14887         }else{
14888             enc = "s:" + v;
14889         }
14890         return escape(enc);        
14891     }
14892 });
14893
14894 /*
14895  * Based on:
14896  * Ext JS Library 1.1.1
14897  * Copyright(c) 2006-2007, Ext JS, LLC.
14898  *
14899  * Originally Released Under LGPL - original licence link has changed is not relivant.
14900  *
14901  * Fork - LGPL
14902  * <script type="text/javascript">
14903  */
14904 /**
14905  * @class Roo.state.Manager
14906  * This is the global state manager. By default all components that are "state aware" check this class
14907  * for state information if you don't pass them a custom state provider. In order for this class
14908  * to be useful, it must be initialized with a provider when your application initializes.
14909  <pre><code>
14910 // in your initialization function
14911 init : function(){
14912    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14913    ...
14914    // supposed you have a {@link Roo.BorderLayout}
14915    var layout = new Roo.BorderLayout(...);
14916    layout.restoreState();
14917    // or a {Roo.BasicDialog}
14918    var dialog = new Roo.BasicDialog(...);
14919    dialog.restoreState();
14920  </code></pre>
14921  * @singleton
14922  */
14923 Roo.state.Manager = function(){
14924     var provider = new Roo.state.Provider();
14925     
14926     return {
14927         /**
14928          * Configures the default state provider for your application
14929          * @param {Provider} stateProvider The state provider to set
14930          */
14931         setProvider : function(stateProvider){
14932             provider = stateProvider;
14933         },
14934         
14935         /**
14936          * Returns the current value for a key
14937          * @param {String} name The key name
14938          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14939          * @return {Mixed} The state data
14940          */
14941         get : function(key, defaultValue){
14942             return provider.get(key, defaultValue);
14943         },
14944         
14945         /**
14946          * Sets the value for a key
14947          * @param {String} name The key name
14948          * @param {Mixed} value The state data
14949          */
14950          set : function(key, value){
14951             provider.set(key, value);
14952         },
14953         
14954         /**
14955          * Clears a value from the state
14956          * @param {String} name The key name
14957          */
14958         clear : function(key){
14959             provider.clear(key);
14960         },
14961         
14962         /**
14963          * Gets the currently configured state provider
14964          * @return {Provider} The state provider
14965          */
14966         getProvider : function(){
14967             return provider;
14968         }
14969     };
14970 }();
14971 /*
14972  * Based on:
14973  * Ext JS Library 1.1.1
14974  * Copyright(c) 2006-2007, Ext JS, LLC.
14975  *
14976  * Originally Released Under LGPL - original licence link has changed is not relivant.
14977  *
14978  * Fork - LGPL
14979  * <script type="text/javascript">
14980  */
14981 /**
14982  * @class Roo.state.CookieProvider
14983  * @extends Roo.state.Provider
14984  * The default Provider implementation which saves state via cookies.
14985  * <br />Usage:
14986  <pre><code>
14987    var cp = new Roo.state.CookieProvider({
14988        path: "/cgi-bin/",
14989        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14990        domain: "roojs.com"
14991    })
14992    Roo.state.Manager.setProvider(cp);
14993  </code></pre>
14994  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14995  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14996  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14997  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14998  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14999  * domain the page is running on including the 'www' like 'www.roojs.com')
15000  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15001  * @constructor
15002  * Create a new CookieProvider
15003  * @param {Object} config The configuration object
15004  */
15005 Roo.state.CookieProvider = function(config){
15006     Roo.state.CookieProvider.superclass.constructor.call(this);
15007     this.path = "/";
15008     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15009     this.domain = null;
15010     this.secure = false;
15011     Roo.apply(this, config);
15012     this.state = this.readCookies();
15013 };
15014
15015 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15016     // private
15017     set : function(name, value){
15018         if(typeof value == "undefined" || value === null){
15019             this.clear(name);
15020             return;
15021         }
15022         this.setCookie(name, value);
15023         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15024     },
15025
15026     // private
15027     clear : function(name){
15028         this.clearCookie(name);
15029         Roo.state.CookieProvider.superclass.clear.call(this, name);
15030     },
15031
15032     // private
15033     readCookies : function(){
15034         var cookies = {};
15035         var c = document.cookie + ";";
15036         var re = /\s?(.*?)=(.*?);/g;
15037         var matches;
15038         while((matches = re.exec(c)) != null){
15039             var name = matches[1];
15040             var value = matches[2];
15041             if(name && name.substring(0,3) == "ys-"){
15042                 cookies[name.substr(3)] = this.decodeValue(value);
15043             }
15044         }
15045         return cookies;
15046     },
15047
15048     // private
15049     setCookie : function(name, value){
15050         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15051            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15052            ((this.path == null) ? "" : ("; path=" + this.path)) +
15053            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15054            ((this.secure == true) ? "; secure" : "");
15055     },
15056
15057     // private
15058     clearCookie : function(name){
15059         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15060            ((this.path == null) ? "" : ("; path=" + this.path)) +
15061            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15062            ((this.secure == true) ? "; secure" : "");
15063     }
15064 });/*
15065  * Based on:
15066  * Ext JS Library 1.1.1
15067  * Copyright(c) 2006-2007, Ext JS, LLC.
15068  *
15069  * Originally Released Under LGPL - original licence link has changed is not relivant.
15070  *
15071  * Fork - LGPL
15072  * <script type="text/javascript">
15073  */
15074  
15075
15076 /**
15077  * @class Roo.ComponentMgr
15078  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15079  * @singleton
15080  */
15081 Roo.ComponentMgr = function(){
15082     var all = new Roo.util.MixedCollection();
15083
15084     return {
15085         /**
15086          * Registers a component.
15087          * @param {Roo.Component} c The component
15088          */
15089         register : function(c){
15090             all.add(c);
15091         },
15092
15093         /**
15094          * Unregisters a component.
15095          * @param {Roo.Component} c The component
15096          */
15097         unregister : function(c){
15098             all.remove(c);
15099         },
15100
15101         /**
15102          * Returns a component by id
15103          * @param {String} id The component id
15104          */
15105         get : function(id){
15106             return all.get(id);
15107         },
15108
15109         /**
15110          * Registers a function that will be called when a specified component is added to ComponentMgr
15111          * @param {String} id The component id
15112          * @param {Funtction} fn The callback function
15113          * @param {Object} scope The scope of the callback
15114          */
15115         onAvailable : function(id, fn, scope){
15116             all.on("add", function(index, o){
15117                 if(o.id == id){
15118                     fn.call(scope || o, o);
15119                     all.un("add", fn, scope);
15120                 }
15121             });
15122         }
15123     };
15124 }();/*
15125  * Based on:
15126  * Ext JS Library 1.1.1
15127  * Copyright(c) 2006-2007, Ext JS, LLC.
15128  *
15129  * Originally Released Under LGPL - original licence link has changed is not relivant.
15130  *
15131  * Fork - LGPL
15132  * <script type="text/javascript">
15133  */
15134  
15135 /**
15136  * @class Roo.Component
15137  * @extends Roo.util.Observable
15138  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15139  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15140  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15141  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15142  * All visual components (widgets) that require rendering into a layout should subclass Component.
15143  * @constructor
15144  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15145  * 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
15146  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15147  */
15148 Roo.Component = function(config){
15149     config = config || {};
15150     if(config.tagName || config.dom || typeof config == "string"){ // element object
15151         config = {el: config, id: config.id || config};
15152     }
15153     this.initialConfig = config;
15154
15155     Roo.apply(this, config);
15156     this.addEvents({
15157         /**
15158          * @event disable
15159          * Fires after the component is disabled.
15160              * @param {Roo.Component} this
15161              */
15162         disable : true,
15163         /**
15164          * @event enable
15165          * Fires after the component is enabled.
15166              * @param {Roo.Component} this
15167              */
15168         enable : true,
15169         /**
15170          * @event beforeshow
15171          * Fires before the component is shown.  Return false to stop the show.
15172              * @param {Roo.Component} this
15173              */
15174         beforeshow : true,
15175         /**
15176          * @event show
15177          * Fires after the component is shown.
15178              * @param {Roo.Component} this
15179              */
15180         show : true,
15181         /**
15182          * @event beforehide
15183          * Fires before the component is hidden. Return false to stop the hide.
15184              * @param {Roo.Component} this
15185              */
15186         beforehide : true,
15187         /**
15188          * @event hide
15189          * Fires after the component is hidden.
15190              * @param {Roo.Component} this
15191              */
15192         hide : true,
15193         /**
15194          * @event beforerender
15195          * Fires before the component is rendered. Return false to stop the render.
15196              * @param {Roo.Component} this
15197              */
15198         beforerender : true,
15199         /**
15200          * @event render
15201          * Fires after the component is rendered.
15202              * @param {Roo.Component} this
15203              */
15204         render : true,
15205         /**
15206          * @event beforedestroy
15207          * Fires before the component is destroyed. Return false to stop the destroy.
15208              * @param {Roo.Component} this
15209              */
15210         beforedestroy : true,
15211         /**
15212          * @event destroy
15213          * Fires after the component is destroyed.
15214              * @param {Roo.Component} this
15215              */
15216         destroy : true
15217     });
15218     if(!this.id){
15219         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15220     }
15221     Roo.ComponentMgr.register(this);
15222     Roo.Component.superclass.constructor.call(this);
15223     this.initComponent();
15224     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15225         this.render(this.renderTo);
15226         delete this.renderTo;
15227     }
15228 };
15229
15230 /** @private */
15231 Roo.Component.AUTO_ID = 1000;
15232
15233 Roo.extend(Roo.Component, Roo.util.Observable, {
15234     /**
15235      * @scope Roo.Component.prototype
15236      * @type {Boolean}
15237      * true if this component is hidden. Read-only.
15238      */
15239     hidden : false,
15240     /**
15241      * @type {Boolean}
15242      * true if this component is disabled. Read-only.
15243      */
15244     disabled : false,
15245     /**
15246      * @type {Boolean}
15247      * true if this component has been rendered. Read-only.
15248      */
15249     rendered : false,
15250     
15251     /** @cfg {String} disableClass
15252      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15253      */
15254     disabledClass : "x-item-disabled",
15255         /** @cfg {Boolean} allowDomMove
15256          * Whether the component can move the Dom node when rendering (defaults to true).
15257          */
15258     allowDomMove : true,
15259     /** @cfg {String} hideMode (display|visibility)
15260      * How this component should hidden. Supported values are
15261      * "visibility" (css visibility), "offsets" (negative offset position) and
15262      * "display" (css display) - defaults to "display".
15263      */
15264     hideMode: 'display',
15265
15266     /** @private */
15267     ctype : "Roo.Component",
15268
15269     /**
15270      * @cfg {String} actionMode 
15271      * which property holds the element that used for  hide() / show() / disable() / enable()
15272      * default is 'el' 
15273      */
15274     actionMode : "el",
15275
15276     /** @private */
15277     getActionEl : function(){
15278         return this[this.actionMode];
15279     },
15280
15281     initComponent : Roo.emptyFn,
15282     /**
15283      * If this is a lazy rendering component, render it to its container element.
15284      * @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.
15285      */
15286     render : function(container, position){
15287         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15288             if(!container && this.el){
15289                 this.el = Roo.get(this.el);
15290                 container = this.el.dom.parentNode;
15291                 this.allowDomMove = false;
15292             }
15293             this.container = Roo.get(container);
15294             this.rendered = true;
15295             if(position !== undefined){
15296                 if(typeof position == 'number'){
15297                     position = this.container.dom.childNodes[position];
15298                 }else{
15299                     position = Roo.getDom(position);
15300                 }
15301             }
15302             this.onRender(this.container, position || null);
15303             if(this.cls){
15304                 this.el.addClass(this.cls);
15305                 delete this.cls;
15306             }
15307             if(this.style){
15308                 this.el.applyStyles(this.style);
15309                 delete this.style;
15310             }
15311             this.fireEvent("render", this);
15312             this.afterRender(this.container);
15313             if(this.hidden){
15314                 this.hide();
15315             }
15316             if(this.disabled){
15317                 this.disable();
15318             }
15319         }
15320         return this;
15321     },
15322
15323     /** @private */
15324     // default function is not really useful
15325     onRender : function(ct, position){
15326         if(this.el){
15327             this.el = Roo.get(this.el);
15328             if(this.allowDomMove !== false){
15329                 ct.dom.insertBefore(this.el.dom, position);
15330             }
15331         }
15332     },
15333
15334     /** @private */
15335     getAutoCreate : function(){
15336         var cfg = typeof this.autoCreate == "object" ?
15337                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15338         if(this.id && !cfg.id){
15339             cfg.id = this.id;
15340         }
15341         return cfg;
15342     },
15343
15344     /** @private */
15345     afterRender : Roo.emptyFn,
15346
15347     /**
15348      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15349      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15350      */
15351     destroy : function(){
15352         if(this.fireEvent("beforedestroy", this) !== false){
15353             this.purgeListeners();
15354             this.beforeDestroy();
15355             if(this.rendered){
15356                 this.el.removeAllListeners();
15357                 this.el.remove();
15358                 if(this.actionMode == "container"){
15359                     this.container.remove();
15360                 }
15361             }
15362             this.onDestroy();
15363             Roo.ComponentMgr.unregister(this);
15364             this.fireEvent("destroy", this);
15365         }
15366     },
15367
15368         /** @private */
15369     beforeDestroy : function(){
15370
15371     },
15372
15373         /** @private */
15374         onDestroy : function(){
15375
15376     },
15377
15378     /**
15379      * Returns the underlying {@link Roo.Element}.
15380      * @return {Roo.Element} The element
15381      */
15382     getEl : function(){
15383         return this.el;
15384     },
15385
15386     /**
15387      * Returns the id of this component.
15388      * @return {String}
15389      */
15390     getId : function(){
15391         return this.id;
15392     },
15393
15394     /**
15395      * Try to focus this component.
15396      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15397      * @return {Roo.Component} this
15398      */
15399     focus : function(selectText){
15400         if(this.rendered){
15401             this.el.focus();
15402             if(selectText === true){
15403                 this.el.dom.select();
15404             }
15405         }
15406         return this;
15407     },
15408
15409     /** @private */
15410     blur : function(){
15411         if(this.rendered){
15412             this.el.blur();
15413         }
15414         return this;
15415     },
15416
15417     /**
15418      * Disable this component.
15419      * @return {Roo.Component} this
15420      */
15421     disable : function(){
15422         if(this.rendered){
15423             this.onDisable();
15424         }
15425         this.disabled = true;
15426         this.fireEvent("disable", this);
15427         return this;
15428     },
15429
15430         // private
15431     onDisable : function(){
15432         this.getActionEl().addClass(this.disabledClass);
15433         this.el.dom.disabled = true;
15434     },
15435
15436     /**
15437      * Enable this component.
15438      * @return {Roo.Component} this
15439      */
15440     enable : function(){
15441         if(this.rendered){
15442             this.onEnable();
15443         }
15444         this.disabled = false;
15445         this.fireEvent("enable", this);
15446         return this;
15447     },
15448
15449         // private
15450     onEnable : function(){
15451         this.getActionEl().removeClass(this.disabledClass);
15452         this.el.dom.disabled = false;
15453     },
15454
15455     /**
15456      * Convenience function for setting disabled/enabled by boolean.
15457      * @param {Boolean} disabled
15458      */
15459     setDisabled : function(disabled){
15460         this[disabled ? "disable" : "enable"]();
15461     },
15462
15463     /**
15464      * Show this component.
15465      * @return {Roo.Component} this
15466      */
15467     show: function(){
15468         if(this.fireEvent("beforeshow", this) !== false){
15469             this.hidden = false;
15470             if(this.rendered){
15471                 this.onShow();
15472             }
15473             this.fireEvent("show", this);
15474         }
15475         return this;
15476     },
15477
15478     // private
15479     onShow : function(){
15480         var ae = this.getActionEl();
15481         if(this.hideMode == 'visibility'){
15482             ae.dom.style.visibility = "visible";
15483         }else if(this.hideMode == 'offsets'){
15484             ae.removeClass('x-hidden');
15485         }else{
15486             ae.dom.style.display = "";
15487         }
15488     },
15489
15490     /**
15491      * Hide this component.
15492      * @return {Roo.Component} this
15493      */
15494     hide: function(){
15495         if(this.fireEvent("beforehide", this) !== false){
15496             this.hidden = true;
15497             if(this.rendered){
15498                 this.onHide();
15499             }
15500             this.fireEvent("hide", this);
15501         }
15502         return this;
15503     },
15504
15505     // private
15506     onHide : function(){
15507         var ae = this.getActionEl();
15508         if(this.hideMode == 'visibility'){
15509             ae.dom.style.visibility = "hidden";
15510         }else if(this.hideMode == 'offsets'){
15511             ae.addClass('x-hidden');
15512         }else{
15513             ae.dom.style.display = "none";
15514         }
15515     },
15516
15517     /**
15518      * Convenience function to hide or show this component by boolean.
15519      * @param {Boolean} visible True to show, false to hide
15520      * @return {Roo.Component} this
15521      */
15522     setVisible: function(visible){
15523         if(visible) {
15524             this.show();
15525         }else{
15526             this.hide();
15527         }
15528         return this;
15529     },
15530
15531     /**
15532      * Returns true if this component is visible.
15533      */
15534     isVisible : function(){
15535         return this.getActionEl().isVisible();
15536     },
15537
15538     cloneConfig : function(overrides){
15539         overrides = overrides || {};
15540         var id = overrides.id || Roo.id();
15541         var cfg = Roo.applyIf(overrides, this.initialConfig);
15542         cfg.id = id; // prevent dup id
15543         return new this.constructor(cfg);
15544     }
15545 });/*
15546  * Based on:
15547  * Ext JS Library 1.1.1
15548  * Copyright(c) 2006-2007, Ext JS, LLC.
15549  *
15550  * Originally Released Under LGPL - original licence link has changed is not relivant.
15551  *
15552  * Fork - LGPL
15553  * <script type="text/javascript">
15554  */
15555
15556 /**
15557  * @class Roo.BoxComponent
15558  * @extends Roo.Component
15559  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15560  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15561  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15562  * layout containers.
15563  * @constructor
15564  * @param {Roo.Element/String/Object} config The configuration options.
15565  */
15566 Roo.BoxComponent = function(config){
15567     Roo.Component.call(this, config);
15568     this.addEvents({
15569         /**
15570          * @event resize
15571          * Fires after the component is resized.
15572              * @param {Roo.Component} this
15573              * @param {Number} adjWidth The box-adjusted width that was set
15574              * @param {Number} adjHeight The box-adjusted height that was set
15575              * @param {Number} rawWidth The width that was originally specified
15576              * @param {Number} rawHeight The height that was originally specified
15577              */
15578         resize : true,
15579         /**
15580          * @event move
15581          * Fires after the component is moved.
15582              * @param {Roo.Component} this
15583              * @param {Number} x The new x position
15584              * @param {Number} y The new y position
15585              */
15586         move : true
15587     });
15588 };
15589
15590 Roo.extend(Roo.BoxComponent, Roo.Component, {
15591     // private, set in afterRender to signify that the component has been rendered
15592     boxReady : false,
15593     // private, used to defer height settings to subclasses
15594     deferHeight: false,
15595     /** @cfg {Number} width
15596      * width (optional) size of component
15597      */
15598      /** @cfg {Number} height
15599      * height (optional) size of component
15600      */
15601      
15602     /**
15603      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15604      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15605      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15606      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15607      * @return {Roo.BoxComponent} this
15608      */
15609     setSize : function(w, h){
15610         // support for standard size objects
15611         if(typeof w == 'object'){
15612             h = w.height;
15613             w = w.width;
15614         }
15615         // not rendered
15616         if(!this.boxReady){
15617             this.width = w;
15618             this.height = h;
15619             return this;
15620         }
15621
15622         // prevent recalcs when not needed
15623         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15624             return this;
15625         }
15626         this.lastSize = {width: w, height: h};
15627
15628         var adj = this.adjustSize(w, h);
15629         var aw = adj.width, ah = adj.height;
15630         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15631             var rz = this.getResizeEl();
15632             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15633                 rz.setSize(aw, ah);
15634             }else if(!this.deferHeight && ah !== undefined){
15635                 rz.setHeight(ah);
15636             }else if(aw !== undefined){
15637                 rz.setWidth(aw);
15638             }
15639             this.onResize(aw, ah, w, h);
15640             this.fireEvent('resize', this, aw, ah, w, h);
15641         }
15642         return this;
15643     },
15644
15645     /**
15646      * Gets the current size of the component's underlying element.
15647      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15648      */
15649     getSize : function(){
15650         return this.el.getSize();
15651     },
15652
15653     /**
15654      * Gets the current XY position of the component's underlying element.
15655      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15656      * @return {Array} The XY position of the element (e.g., [100, 200])
15657      */
15658     getPosition : function(local){
15659         if(local === true){
15660             return [this.el.getLeft(true), this.el.getTop(true)];
15661         }
15662         return this.xy || this.el.getXY();
15663     },
15664
15665     /**
15666      * Gets the current box measurements of the component's underlying element.
15667      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15668      * @returns {Object} box An object in the format {x, y, width, height}
15669      */
15670     getBox : function(local){
15671         var s = this.el.getSize();
15672         if(local){
15673             s.x = this.el.getLeft(true);
15674             s.y = this.el.getTop(true);
15675         }else{
15676             var xy = this.xy || this.el.getXY();
15677             s.x = xy[0];
15678             s.y = xy[1];
15679         }
15680         return s;
15681     },
15682
15683     /**
15684      * Sets the current box measurements of the component's underlying element.
15685      * @param {Object} box An object in the format {x, y, width, height}
15686      * @returns {Roo.BoxComponent} this
15687      */
15688     updateBox : function(box){
15689         this.setSize(box.width, box.height);
15690         this.setPagePosition(box.x, box.y);
15691         return this;
15692     },
15693
15694     // protected
15695     getResizeEl : function(){
15696         return this.resizeEl || this.el;
15697     },
15698
15699     // protected
15700     getPositionEl : function(){
15701         return this.positionEl || this.el;
15702     },
15703
15704     /**
15705      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15706      * This method fires the move event.
15707      * @param {Number} left The new left
15708      * @param {Number} top The new top
15709      * @returns {Roo.BoxComponent} this
15710      */
15711     setPosition : function(x, y){
15712         this.x = x;
15713         this.y = y;
15714         if(!this.boxReady){
15715             return this;
15716         }
15717         var adj = this.adjustPosition(x, y);
15718         var ax = adj.x, ay = adj.y;
15719
15720         var el = this.getPositionEl();
15721         if(ax !== undefined || ay !== undefined){
15722             if(ax !== undefined && ay !== undefined){
15723                 el.setLeftTop(ax, ay);
15724             }else if(ax !== undefined){
15725                 el.setLeft(ax);
15726             }else if(ay !== undefined){
15727                 el.setTop(ay);
15728             }
15729             this.onPosition(ax, ay);
15730             this.fireEvent('move', this, ax, ay);
15731         }
15732         return this;
15733     },
15734
15735     /**
15736      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15737      * This method fires the move event.
15738      * @param {Number} x The new x position
15739      * @param {Number} y The new y position
15740      * @returns {Roo.BoxComponent} this
15741      */
15742     setPagePosition : function(x, y){
15743         this.pageX = x;
15744         this.pageY = y;
15745         if(!this.boxReady){
15746             return;
15747         }
15748         if(x === undefined || y === undefined){ // cannot translate undefined points
15749             return;
15750         }
15751         var p = this.el.translatePoints(x, y);
15752         this.setPosition(p.left, p.top);
15753         return this;
15754     },
15755
15756     // private
15757     onRender : function(ct, position){
15758         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15759         if(this.resizeEl){
15760             this.resizeEl = Roo.get(this.resizeEl);
15761         }
15762         if(this.positionEl){
15763             this.positionEl = Roo.get(this.positionEl);
15764         }
15765     },
15766
15767     // private
15768     afterRender : function(){
15769         Roo.BoxComponent.superclass.afterRender.call(this);
15770         this.boxReady = true;
15771         this.setSize(this.width, this.height);
15772         if(this.x || this.y){
15773             this.setPosition(this.x, this.y);
15774         }
15775         if(this.pageX || this.pageY){
15776             this.setPagePosition(this.pageX, this.pageY);
15777         }
15778     },
15779
15780     /**
15781      * Force the component's size to recalculate based on the underlying element's current height and width.
15782      * @returns {Roo.BoxComponent} this
15783      */
15784     syncSize : function(){
15785         delete this.lastSize;
15786         this.setSize(this.el.getWidth(), this.el.getHeight());
15787         return this;
15788     },
15789
15790     /**
15791      * Called after the component is resized, this method is empty by default but can be implemented by any
15792      * subclass that needs to perform custom logic after a resize occurs.
15793      * @param {Number} adjWidth The box-adjusted width that was set
15794      * @param {Number} adjHeight The box-adjusted height that was set
15795      * @param {Number} rawWidth The width that was originally specified
15796      * @param {Number} rawHeight The height that was originally specified
15797      */
15798     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15799
15800     },
15801
15802     /**
15803      * Called after the component is moved, this method is empty by default but can be implemented by any
15804      * subclass that needs to perform custom logic after a move occurs.
15805      * @param {Number} x The new x position
15806      * @param {Number} y The new y position
15807      */
15808     onPosition : function(x, y){
15809
15810     },
15811
15812     // private
15813     adjustSize : function(w, h){
15814         if(this.autoWidth){
15815             w = 'auto';
15816         }
15817         if(this.autoHeight){
15818             h = 'auto';
15819         }
15820         return {width : w, height: h};
15821     },
15822
15823     // private
15824     adjustPosition : function(x, y){
15825         return {x : x, y: y};
15826     }
15827 });/*
15828  * Original code for Roojs - LGPL
15829  * <script type="text/javascript">
15830  */
15831  
15832 /**
15833  * @class Roo.XComponent
15834  * A delayed Element creator...
15835  * Or a way to group chunks of interface together.
15836  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15837  *  used in conjunction with XComponent.build() it will create an instance of each element,
15838  *  then call addxtype() to build the User interface.
15839  * 
15840  * Mypart.xyx = new Roo.XComponent({
15841
15842     parent : 'Mypart.xyz', // empty == document.element.!!
15843     order : '001',
15844     name : 'xxxx'
15845     region : 'xxxx'
15846     disabled : function() {} 
15847      
15848     tree : function() { // return an tree of xtype declared components
15849         var MODULE = this;
15850         return 
15851         {
15852             xtype : 'NestedLayoutPanel',
15853             // technicall
15854         }
15855      ]
15856  *})
15857  *
15858  *
15859  * It can be used to build a big heiracy, with parent etc.
15860  * or you can just use this to render a single compoent to a dom element
15861  * MYPART.render(Roo.Element | String(id) | dom_element )
15862  *
15863  *
15864  * Usage patterns.
15865  *
15866  * Classic Roo
15867  *
15868  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15869  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15870  *
15871  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15872  *
15873  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15874  * - if mulitple topModules exist, the last one is defined as the top module.
15875  *
15876  * Embeded Roo
15877  * 
15878  * When the top level or multiple modules are to embedded into a existing HTML page,
15879  * the parent element can container '#id' of the element where the module will be drawn.
15880  *
15881  * Bootstrap Roo
15882  *
15883  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15884  * it relies more on a include mechanism, where sub modules are included into an outer page.
15885  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15886  * 
15887  * Bootstrap Roo Included elements
15888  *
15889  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15890  * hence confusing the component builder as it thinks there are multiple top level elements. 
15891  *
15892  * 
15893  * 
15894  * @extends Roo.util.Observable
15895  * @constructor
15896  * @param cfg {Object} configuration of component
15897  * 
15898  */
15899 Roo.XComponent = function(cfg) {
15900     Roo.apply(this, cfg);
15901     this.addEvents({ 
15902         /**
15903              * @event built
15904              * Fires when this the componnt is built
15905              * @param {Roo.XComponent} c the component
15906              */
15907         'built' : true
15908         
15909     });
15910     this.region = this.region || 'center'; // default..
15911     Roo.XComponent.register(this);
15912     this.modules = false;
15913     this.el = false; // where the layout goes..
15914     
15915     
15916 }
15917 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15918     /**
15919      * @property el
15920      * The created element (with Roo.factory())
15921      * @type {Roo.Layout}
15922      */
15923     el  : false,
15924     
15925     /**
15926      * @property el
15927      * for BC  - use el in new code
15928      * @type {Roo.Layout}
15929      */
15930     panel : false,
15931     
15932     /**
15933      * @property layout
15934      * for BC  - use el in new code
15935      * @type {Roo.Layout}
15936      */
15937     layout : false,
15938     
15939      /**
15940      * @cfg {Function|boolean} disabled
15941      * If this module is disabled by some rule, return true from the funtion
15942      */
15943     disabled : false,
15944     
15945     /**
15946      * @cfg {String} parent 
15947      * Name of parent element which it get xtype added to..
15948      */
15949     parent: false,
15950     
15951     /**
15952      * @cfg {String} order
15953      * Used to set the order in which elements are created (usefull for multiple tabs)
15954      */
15955     
15956     order : false,
15957     /**
15958      * @cfg {String} name
15959      * String to display while loading.
15960      */
15961     name : false,
15962     /**
15963      * @cfg {String} region
15964      * Region to render component to (defaults to center)
15965      */
15966     region : 'center',
15967     
15968     /**
15969      * @cfg {Array} items
15970      * A single item array - the first element is the root of the tree..
15971      * It's done this way to stay compatible with the Xtype system...
15972      */
15973     items : false,
15974     
15975     /**
15976      * @property _tree
15977      * The method that retuns the tree of parts that make up this compoennt 
15978      * @type {function}
15979      */
15980     _tree  : false,
15981     
15982      /**
15983      * render
15984      * render element to dom or tree
15985      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15986      */
15987     
15988     render : function(el)
15989     {
15990         
15991         el = el || false;
15992         var hp = this.parent ? 1 : 0;
15993         Roo.debug &&  Roo.log(this);
15994         
15995         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15996             // if parent is a '#.....' string, then let's use that..
15997             var ename = this.parent.substr(1);
15998             this.parent = false;
15999             Roo.debug && Roo.log(ename);
16000             switch (ename) {
16001                 case 'bootstrap-body' :
16002                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
16003                         this.parent = { el :  new  Roo.bootstrap.Body() };
16004                         Roo.debug && Roo.log("setting el to doc body");
16005                          
16006                     } else {
16007                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16008                     }
16009                     break;
16010                 case 'bootstrap':
16011                     this.parent = { el : true};
16012                     // fall through
16013                 default:
16014                     el = Roo.get(ename);
16015                     break;
16016             }
16017                 
16018             
16019             if (!el && !this.parent) {
16020                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16021                 return;
16022             }
16023         }
16024         Roo.debug && Roo.log("EL:");
16025         Roo.debug && Roo.log(el);
16026         Roo.debug && Roo.log("this.parent.el:");
16027         Roo.debug && Roo.log(this.parent.el);
16028         
16029         var tree = this._tree ? this._tree() : this.tree();
16030
16031         // altertive root elements ??? - we need a better way to indicate these.
16032         var is_alt = Roo.XComponent.is_alt || (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16033                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16034         
16035         if (!this.parent && is_alt) {
16036             //el = Roo.get(document.body);
16037             this.parent = { el : true };
16038         }
16039             
16040             
16041         
16042         if (!this.parent) {
16043             
16044             Roo.debug && Roo.log("no parent - creating one");
16045             
16046             el = el ? Roo.get(el) : false;      
16047             
16048             // it's a top level one..
16049             this.parent =  {
16050                 el : new Roo.BorderLayout(el || document.body, {
16051                 
16052                      center: {
16053                          titlebar: false,
16054                          autoScroll:false,
16055                          closeOnTab: true,
16056                          tabPosition: 'top',
16057                           //resizeTabs: true,
16058                          alwaysShowTabs: el && hp? false :  true,
16059                          hideTabs: el || !hp ? true :  false,
16060                          minTabWidth: 140
16061                      }
16062                  })
16063             };
16064         }
16065         
16066         if (!this.parent.el) {
16067                 // probably an old style ctor, which has been disabled.
16068                 return;
16069
16070         }
16071                 // The 'tree' method is  '_tree now' 
16072             
16073         tree.region = tree.region || this.region;
16074         var is_body = false;
16075         if (this.parent.el === true) {
16076             // bootstrap... - body..
16077             this.parent.el = Roo.factory(tree);
16078             is_body = true;
16079         }
16080         
16081         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16082         this.fireEvent('built', this);
16083         
16084         this.panel = this.el;
16085         this.layout = this.panel.layout;
16086         this.parentLayout = this.parent.layout  || false;  
16087          
16088     }
16089     
16090 });
16091
16092 Roo.apply(Roo.XComponent, {
16093     /**
16094      * @property  hideProgress
16095      * true to disable the building progress bar.. usefull on single page renders.
16096      * @type Boolean
16097      */
16098     hideProgress : false,
16099     /**
16100      * @property  buildCompleted
16101      * True when the builder has completed building the interface.
16102      * @type Boolean
16103      */
16104     buildCompleted : false,
16105      
16106     /**
16107      * @property  topModule
16108      * the upper most module - uses document.element as it's constructor.
16109      * @type Object
16110      */
16111      
16112     topModule  : false,
16113       
16114     /**
16115      * @property  modules
16116      * array of modules to be created by registration system.
16117      * @type {Array} of Roo.XComponent
16118      */
16119     
16120     modules : [],
16121     /**
16122      * @property  elmodules
16123      * array of modules to be created by which use #ID 
16124      * @type {Array} of Roo.XComponent
16125      */
16126      
16127     elmodules : [],
16128
16129      /**
16130      * @property  is_alt
16131      * Is an alternative Root - normally used by bootstrap or other systems,
16132      *    where the top element in the tree can wrap 'body' 
16133      * @type {boolean}  (default false)
16134      */
16135      
16136     is_alt : false,
16137     /**
16138      * @property  build_from_html
16139      * Build elements from html - used by bootstrap HTML stuff 
16140      *    - this is cleared after build is completed
16141      * @type {boolean}    (default false)
16142      */
16143      
16144     build_from_html : false,
16145     /**
16146      * Register components to be built later.
16147      *
16148      * This solves the following issues
16149      * - Building is not done on page load, but after an authentication process has occured.
16150      * - Interface elements are registered on page load
16151      * - Parent Interface elements may not be loaded before child, so this handles that..
16152      * 
16153      *
16154      * example:
16155      * 
16156      * MyApp.register({
16157           order : '000001',
16158           module : 'Pman.Tab.projectMgr',
16159           region : 'center',
16160           parent : 'Pman.layout',
16161           disabled : false,  // or use a function..
16162         })
16163      
16164      * * @param {Object} details about module
16165      */
16166     register : function(obj) {
16167                 
16168         Roo.XComponent.event.fireEvent('register', obj);
16169         switch(typeof(obj.disabled) ) {
16170                 
16171             case 'undefined':
16172                 break;
16173             
16174             case 'function':
16175                 if ( obj.disabled() ) {
16176                         return;
16177                 }
16178                 break;
16179             
16180             default:
16181                 if (obj.disabled) {
16182                         return;
16183                 }
16184                 break;
16185         }
16186                 
16187         this.modules.push(obj);
16188          
16189     },
16190     /**
16191      * convert a string to an object..
16192      * eg. 'AAA.BBB' -> finds AAA.BBB
16193
16194      */
16195     
16196     toObject : function(str)
16197     {
16198         if (!str || typeof(str) == 'object') {
16199             return str;
16200         }
16201         if (str.substring(0,1) == '#') {
16202             return str;
16203         }
16204
16205         var ar = str.split('.');
16206         var rt, o;
16207         rt = ar.shift();
16208             /** eval:var:o */
16209         try {
16210             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16211         } catch (e) {
16212             throw "Module not found : " + str;
16213         }
16214         
16215         if (o === false) {
16216             throw "Module not found : " + str;
16217         }
16218         Roo.each(ar, function(e) {
16219             if (typeof(o[e]) == 'undefined') {
16220                 throw "Module not found : " + str;
16221             }
16222             o = o[e];
16223         });
16224         
16225         return o;
16226         
16227     },
16228     
16229     
16230     /**
16231      * move modules into their correct place in the tree..
16232      * 
16233      */
16234     preBuild : function ()
16235     {
16236         var _t = this;
16237         Roo.each(this.modules , function (obj)
16238         {
16239             Roo.XComponent.event.fireEvent('beforebuild', obj);
16240             
16241             var opar = obj.parent;
16242             try { 
16243                 obj.parent = this.toObject(opar);
16244             } catch(e) {
16245                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16246                 return;
16247             }
16248             
16249             if (!obj.parent) {
16250                 Roo.debug && Roo.log("GOT top level module");
16251                 Roo.debug && Roo.log(obj);
16252                 obj.modules = new Roo.util.MixedCollection(false, 
16253                     function(o) { return o.order + '' }
16254                 );
16255                 this.topModule = obj;
16256                 return;
16257             }
16258                         // parent is a string (usually a dom element name..)
16259             if (typeof(obj.parent) == 'string') {
16260                 this.elmodules.push(obj);
16261                 return;
16262             }
16263             if (obj.parent.constructor != Roo.XComponent) {
16264                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16265             }
16266             if (!obj.parent.modules) {
16267                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16268                     function(o) { return o.order + '' }
16269                 );
16270             }
16271             if (obj.parent.disabled) {
16272                 obj.disabled = true;
16273             }
16274             obj.parent.modules.add(obj);
16275         }, this);
16276     },
16277     
16278      /**
16279      * make a list of modules to build.
16280      * @return {Array} list of modules. 
16281      */ 
16282     
16283     buildOrder : function()
16284     {
16285         var _this = this;
16286         var cmp = function(a,b) {   
16287             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16288         };
16289         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16290             throw "No top level modules to build";
16291         }
16292         
16293         // make a flat list in order of modules to build.
16294         var mods = this.topModule ? [ this.topModule ] : [];
16295                 
16296         
16297         // elmodules (is a list of DOM based modules )
16298         Roo.each(this.elmodules, function(e) {
16299             mods.push(e);
16300             if (!this.topModule &&
16301                 typeof(e.parent) == 'string' &&
16302                 e.parent.substring(0,1) == '#' &&
16303                 Roo.get(e.parent.substr(1))
16304                ) {
16305                 
16306                 _this.topModule = e;
16307             }
16308             
16309         });
16310
16311         
16312         // add modules to their parents..
16313         var addMod = function(m) {
16314             Roo.debug && Roo.log("build Order: add: " + m.name);
16315                 
16316             mods.push(m);
16317             if (m.modules && !m.disabled) {
16318                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16319                 m.modules.keySort('ASC',  cmp );
16320                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16321     
16322                 m.modules.each(addMod);
16323             } else {
16324                 Roo.debug && Roo.log("build Order: no child modules");
16325             }
16326             // not sure if this is used any more..
16327             if (m.finalize) {
16328                 m.finalize.name = m.name + " (clean up) ";
16329                 mods.push(m.finalize);
16330             }
16331             
16332         }
16333         if (this.topModule && this.topModule.modules) { 
16334             this.topModule.modules.keySort('ASC',  cmp );
16335             this.topModule.modules.each(addMod);
16336         } 
16337         return mods;
16338     },
16339     
16340      /**
16341      * Build the registered modules.
16342      * @param {Object} parent element.
16343      * @param {Function} optional method to call after module has been added.
16344      * 
16345      */ 
16346    
16347     build : function(opts) 
16348     {
16349         
16350         if (typeof(opts) != 'undefined') {
16351             Roo.apply(this,opts);
16352         }
16353         
16354         this.preBuild();
16355         var mods = this.buildOrder();
16356       
16357         //this.allmods = mods;
16358         //Roo.debug && Roo.log(mods);
16359         //return;
16360         if (!mods.length) { // should not happen
16361             throw "NO modules!!!";
16362         }
16363         
16364         
16365         var msg = "Building Interface...";
16366         // flash it up as modal - so we store the mask!?
16367         if (!this.hideProgress && Roo.MessageBox) {
16368             Roo.MessageBox.show({ title: 'loading' });
16369             Roo.MessageBox.show({
16370                title: "Please wait...",
16371                msg: msg,
16372                width:450,
16373                progress:true,
16374                closable:false,
16375                modal: false
16376               
16377             });
16378         }
16379         var total = mods.length;
16380         
16381         var _this = this;
16382         var progressRun = function() {
16383             if (!mods.length) {
16384                 Roo.debug && Roo.log('hide?');
16385                 if (!this.hideProgress && Roo.MessageBox) {
16386                     Roo.MessageBox.hide();
16387                 }
16388                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16389                 
16390                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16391                 
16392                 // THE END...
16393                 return false;   
16394             }
16395             
16396             var m = mods.shift();
16397             
16398             
16399             Roo.debug && Roo.log(m);
16400             // not sure if this is supported any more.. - modules that are are just function
16401             if (typeof(m) == 'function') { 
16402                 m.call(this);
16403                 return progressRun.defer(10, _this);
16404             } 
16405             
16406             
16407             msg = "Building Interface " + (total  - mods.length) + 
16408                     " of " + total + 
16409                     (m.name ? (' - ' + m.name) : '');
16410                         Roo.debug && Roo.log(msg);
16411             if (!this.hideProgress &&  Roo.MessageBox) { 
16412                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16413             }
16414             
16415          
16416             // is the module disabled?
16417             var disabled = (typeof(m.disabled) == 'function') ?
16418                 m.disabled.call(m.module.disabled) : m.disabled;    
16419             
16420             
16421             if (disabled) {
16422                 return progressRun(); // we do not update the display!
16423             }
16424             
16425             // now build 
16426             
16427                         
16428                         
16429             m.render();
16430             // it's 10 on top level, and 1 on others??? why...
16431             return progressRun.defer(10, _this);
16432              
16433         }
16434         progressRun.defer(1, _this);
16435      
16436         
16437         
16438     },
16439         
16440         
16441         /**
16442          * Event Object.
16443          *
16444          *
16445          */
16446         event: false, 
16447     /**
16448          * wrapper for event.on - aliased later..  
16449          * Typically use to register a event handler for register:
16450          *
16451          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16452          *
16453          */
16454     on : false
16455    
16456     
16457     
16458 });
16459
16460 Roo.XComponent.event = new Roo.util.Observable({
16461                 events : { 
16462                         /**
16463                          * @event register
16464                          * Fires when an Component is registered,
16465                          * set the disable property on the Component to stop registration.
16466                          * @param {Roo.XComponent} c the component being registerd.
16467                          * 
16468                          */
16469                         'register' : true,
16470             /**
16471                          * @event beforebuild
16472                          * Fires before each Component is built
16473                          * can be used to apply permissions.
16474                          * @param {Roo.XComponent} c the component being registerd.
16475                          * 
16476                          */
16477                         'beforebuild' : true,
16478                         /**
16479                          * @event buildcomplete
16480                          * Fires on the top level element when all elements have been built
16481                          * @param {Roo.XComponent} the top level component.
16482                          */
16483                         'buildcomplete' : true
16484                         
16485                 }
16486 });
16487
16488 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16489  /*
16490  * Based on:
16491  * Ext JS Library 1.1.1
16492  * Copyright(c) 2006-2007, Ext JS, LLC.
16493  *
16494  * Originally Released Under LGPL - original licence link has changed is not relivant.
16495  *
16496  * Fork - LGPL
16497  * <script type="text/javascript">
16498  */
16499
16500
16501
16502 /*
16503  * These classes are derivatives of the similarly named classes in the YUI Library.
16504  * The original license:
16505  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16506  * Code licensed under the BSD License:
16507  * http://developer.yahoo.net/yui/license.txt
16508  */
16509
16510 (function() {
16511
16512 var Event=Roo.EventManager;
16513 var Dom=Roo.lib.Dom;
16514
16515 /**
16516  * @class Roo.dd.DragDrop
16517  * @extends Roo.util.Observable
16518  * Defines the interface and base operation of items that that can be
16519  * dragged or can be drop targets.  It was designed to be extended, overriding
16520  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16521  * Up to three html elements can be associated with a DragDrop instance:
16522  * <ul>
16523  * <li>linked element: the element that is passed into the constructor.
16524  * This is the element which defines the boundaries for interaction with
16525  * other DragDrop objects.</li>
16526  * <li>handle element(s): The drag operation only occurs if the element that
16527  * was clicked matches a handle element.  By default this is the linked
16528  * element, but there are times that you will want only a portion of the
16529  * linked element to initiate the drag operation, and the setHandleElId()
16530  * method provides a way to define this.</li>
16531  * <li>drag element: this represents the element that would be moved along
16532  * with the cursor during a drag operation.  By default, this is the linked
16533  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16534  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16535  * </li>
16536  * </ul>
16537  * This class should not be instantiated until the onload event to ensure that
16538  * the associated elements are available.
16539  * The following would define a DragDrop obj that would interact with any
16540  * other DragDrop obj in the "group1" group:
16541  * <pre>
16542  *  dd = new Roo.dd.DragDrop("div1", "group1");
16543  * </pre>
16544  * Since none of the event handlers have been implemented, nothing would
16545  * actually happen if you were to run the code above.  Normally you would
16546  * override this class or one of the default implementations, but you can
16547  * also override the methods you want on an instance of the class...
16548  * <pre>
16549  *  dd.onDragDrop = function(e, id) {
16550  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16551  *  }
16552  * </pre>
16553  * @constructor
16554  * @param {String} id of the element that is linked to this instance
16555  * @param {String} sGroup the group of related DragDrop objects
16556  * @param {object} config an object containing configurable attributes
16557  *                Valid properties for DragDrop:
16558  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16559  */
16560 Roo.dd.DragDrop = function(id, sGroup, config) {
16561     if (id) {
16562         this.init(id, sGroup, config);
16563     }
16564     
16565 };
16566
16567 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16568
16569     /**
16570      * The id of the element associated with this object.  This is what we
16571      * refer to as the "linked element" because the size and position of
16572      * this element is used to determine when the drag and drop objects have
16573      * interacted.
16574      * @property id
16575      * @type String
16576      */
16577     id: null,
16578
16579     /**
16580      * Configuration attributes passed into the constructor
16581      * @property config
16582      * @type object
16583      */
16584     config: null,
16585
16586     /**
16587      * The id of the element that will be dragged.  By default this is same
16588      * as the linked element , but could be changed to another element. Ex:
16589      * Roo.dd.DDProxy
16590      * @property dragElId
16591      * @type String
16592      * @private
16593      */
16594     dragElId: null,
16595
16596     /**
16597      * the id of the element that initiates the drag operation.  By default
16598      * this is the linked element, but could be changed to be a child of this
16599      * element.  This lets us do things like only starting the drag when the
16600      * header element within the linked html element is clicked.
16601      * @property handleElId
16602      * @type String
16603      * @private
16604      */
16605     handleElId: null,
16606
16607     /**
16608      * An associative array of HTML tags that will be ignored if clicked.
16609      * @property invalidHandleTypes
16610      * @type {string: string}
16611      */
16612     invalidHandleTypes: null,
16613
16614     /**
16615      * An associative array of ids for elements that will be ignored if clicked
16616      * @property invalidHandleIds
16617      * @type {string: string}
16618      */
16619     invalidHandleIds: null,
16620
16621     /**
16622      * An indexted array of css class names for elements that will be ignored
16623      * if clicked.
16624      * @property invalidHandleClasses
16625      * @type string[]
16626      */
16627     invalidHandleClasses: null,
16628
16629     /**
16630      * The linked element's absolute X position at the time the drag was
16631      * started
16632      * @property startPageX
16633      * @type int
16634      * @private
16635      */
16636     startPageX: 0,
16637
16638     /**
16639      * The linked element's absolute X position at the time the drag was
16640      * started
16641      * @property startPageY
16642      * @type int
16643      * @private
16644      */
16645     startPageY: 0,
16646
16647     /**
16648      * The group defines a logical collection of DragDrop objects that are
16649      * related.  Instances only get events when interacting with other
16650      * DragDrop object in the same group.  This lets us define multiple
16651      * groups using a single DragDrop subclass if we want.
16652      * @property groups
16653      * @type {string: string}
16654      */
16655     groups: null,
16656
16657     /**
16658      * Individual drag/drop instances can be locked.  This will prevent
16659      * onmousedown start drag.
16660      * @property locked
16661      * @type boolean
16662      * @private
16663      */
16664     locked: false,
16665
16666     /**
16667      * Lock this instance
16668      * @method lock
16669      */
16670     lock: function() { this.locked = true; },
16671
16672     /**
16673      * Unlock this instace
16674      * @method unlock
16675      */
16676     unlock: function() { this.locked = false; },
16677
16678     /**
16679      * By default, all insances can be a drop target.  This can be disabled by
16680      * setting isTarget to false.
16681      * @method isTarget
16682      * @type boolean
16683      */
16684     isTarget: true,
16685
16686     /**
16687      * The padding configured for this drag and drop object for calculating
16688      * the drop zone intersection with this object.
16689      * @method padding
16690      * @type int[]
16691      */
16692     padding: null,
16693
16694     /**
16695      * Cached reference to the linked element
16696      * @property _domRef
16697      * @private
16698      */
16699     _domRef: null,
16700
16701     /**
16702      * Internal typeof flag
16703      * @property __ygDragDrop
16704      * @private
16705      */
16706     __ygDragDrop: true,
16707
16708     /**
16709      * Set to true when horizontal contraints are applied
16710      * @property constrainX
16711      * @type boolean
16712      * @private
16713      */
16714     constrainX: false,
16715
16716     /**
16717      * Set to true when vertical contraints are applied
16718      * @property constrainY
16719      * @type boolean
16720      * @private
16721      */
16722     constrainY: false,
16723
16724     /**
16725      * The left constraint
16726      * @property minX
16727      * @type int
16728      * @private
16729      */
16730     minX: 0,
16731
16732     /**
16733      * The right constraint
16734      * @property maxX
16735      * @type int
16736      * @private
16737      */
16738     maxX: 0,
16739
16740     /**
16741      * The up constraint
16742      * @property minY
16743      * @type int
16744      * @type int
16745      * @private
16746      */
16747     minY: 0,
16748
16749     /**
16750      * The down constraint
16751      * @property maxY
16752      * @type int
16753      * @private
16754      */
16755     maxY: 0,
16756
16757     /**
16758      * Maintain offsets when we resetconstraints.  Set to true when you want
16759      * the position of the element relative to its parent to stay the same
16760      * when the page changes
16761      *
16762      * @property maintainOffset
16763      * @type boolean
16764      */
16765     maintainOffset: false,
16766
16767     /**
16768      * Array of pixel locations the element will snap to if we specified a
16769      * horizontal graduation/interval.  This array is generated automatically
16770      * when you define a tick interval.
16771      * @property xTicks
16772      * @type int[]
16773      */
16774     xTicks: null,
16775
16776     /**
16777      * Array of pixel locations the element will snap to if we specified a
16778      * vertical graduation/interval.  This array is generated automatically
16779      * when you define a tick interval.
16780      * @property yTicks
16781      * @type int[]
16782      */
16783     yTicks: null,
16784
16785     /**
16786      * By default the drag and drop instance will only respond to the primary
16787      * button click (left button for a right-handed mouse).  Set to true to
16788      * allow drag and drop to start with any mouse click that is propogated
16789      * by the browser
16790      * @property primaryButtonOnly
16791      * @type boolean
16792      */
16793     primaryButtonOnly: true,
16794
16795     /**
16796      * The availabe property is false until the linked dom element is accessible.
16797      * @property available
16798      * @type boolean
16799      */
16800     available: false,
16801
16802     /**
16803      * By default, drags can only be initiated if the mousedown occurs in the
16804      * region the linked element is.  This is done in part to work around a
16805      * bug in some browsers that mis-report the mousedown if the previous
16806      * mouseup happened outside of the window.  This property is set to true
16807      * if outer handles are defined.
16808      *
16809      * @property hasOuterHandles
16810      * @type boolean
16811      * @default false
16812      */
16813     hasOuterHandles: false,
16814
16815     /**
16816      * Code that executes immediately before the startDrag event
16817      * @method b4StartDrag
16818      * @private
16819      */
16820     b4StartDrag: function(x, y) { },
16821
16822     /**
16823      * Abstract method called after a drag/drop object is clicked
16824      * and the drag or mousedown time thresholds have beeen met.
16825      * @method startDrag
16826      * @param {int} X click location
16827      * @param {int} Y click location
16828      */
16829     startDrag: function(x, y) { /* override this */ },
16830
16831     /**
16832      * Code that executes immediately before the onDrag event
16833      * @method b4Drag
16834      * @private
16835      */
16836     b4Drag: function(e) { },
16837
16838     /**
16839      * Abstract method called during the onMouseMove event while dragging an
16840      * object.
16841      * @method onDrag
16842      * @param {Event} e the mousemove event
16843      */
16844     onDrag: function(e) { /* override this */ },
16845
16846     /**
16847      * Abstract method called when this element fist begins hovering over
16848      * another DragDrop obj
16849      * @method onDragEnter
16850      * @param {Event} e the mousemove event
16851      * @param {String|DragDrop[]} id In POINT mode, the element
16852      * id this is hovering over.  In INTERSECT mode, an array of one or more
16853      * dragdrop items being hovered over.
16854      */
16855     onDragEnter: function(e, id) { /* override this */ },
16856
16857     /**
16858      * Code that executes immediately before the onDragOver event
16859      * @method b4DragOver
16860      * @private
16861      */
16862     b4DragOver: function(e) { },
16863
16864     /**
16865      * Abstract method called when this element is hovering over another
16866      * DragDrop obj
16867      * @method onDragOver
16868      * @param {Event} e the mousemove event
16869      * @param {String|DragDrop[]} id In POINT mode, the element
16870      * id this is hovering over.  In INTERSECT mode, an array of dd items
16871      * being hovered over.
16872      */
16873     onDragOver: function(e, id) { /* override this */ },
16874
16875     /**
16876      * Code that executes immediately before the onDragOut event
16877      * @method b4DragOut
16878      * @private
16879      */
16880     b4DragOut: function(e) { },
16881
16882     /**
16883      * Abstract method called when we are no longer hovering over an element
16884      * @method onDragOut
16885      * @param {Event} e the mousemove event
16886      * @param {String|DragDrop[]} id In POINT mode, the element
16887      * id this was hovering over.  In INTERSECT mode, an array of dd items
16888      * that the mouse is no longer over.
16889      */
16890     onDragOut: function(e, id) { /* override this */ },
16891
16892     /**
16893      * Code that executes immediately before the onDragDrop event
16894      * @method b4DragDrop
16895      * @private
16896      */
16897     b4DragDrop: function(e) { },
16898
16899     /**
16900      * Abstract method called when this item is dropped on another DragDrop
16901      * obj
16902      * @method onDragDrop
16903      * @param {Event} e the mouseup event
16904      * @param {String|DragDrop[]} id In POINT mode, the element
16905      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16906      * was dropped on.
16907      */
16908     onDragDrop: function(e, id) { /* override this */ },
16909
16910     /**
16911      * Abstract method called when this item is dropped on an area with no
16912      * drop target
16913      * @method onInvalidDrop
16914      * @param {Event} e the mouseup event
16915      */
16916     onInvalidDrop: function(e) { /* override this */ },
16917
16918     /**
16919      * Code that executes immediately before the endDrag event
16920      * @method b4EndDrag
16921      * @private
16922      */
16923     b4EndDrag: function(e) { },
16924
16925     /**
16926      * Fired when we are done dragging the object
16927      * @method endDrag
16928      * @param {Event} e the mouseup event
16929      */
16930     endDrag: function(e) { /* override this */ },
16931
16932     /**
16933      * Code executed immediately before the onMouseDown event
16934      * @method b4MouseDown
16935      * @param {Event} e the mousedown event
16936      * @private
16937      */
16938     b4MouseDown: function(e) {  },
16939
16940     /**
16941      * Event handler that fires when a drag/drop obj gets a mousedown
16942      * @method onMouseDown
16943      * @param {Event} e the mousedown event
16944      */
16945     onMouseDown: function(e) { /* override this */ },
16946
16947     /**
16948      * Event handler that fires when a drag/drop obj gets a mouseup
16949      * @method onMouseUp
16950      * @param {Event} e the mouseup event
16951      */
16952     onMouseUp: function(e) { /* override this */ },
16953
16954     /**
16955      * Override the onAvailable method to do what is needed after the initial
16956      * position was determined.
16957      * @method onAvailable
16958      */
16959     onAvailable: function () {
16960     },
16961
16962     /*
16963      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16964      * @type Object
16965      */
16966     defaultPadding : {left:0, right:0, top:0, bottom:0},
16967
16968     /*
16969      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16970  *
16971  * Usage:
16972  <pre><code>
16973  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16974                 { dragElId: "existingProxyDiv" });
16975  dd.startDrag = function(){
16976      this.constrainTo("parent-id");
16977  };
16978  </code></pre>
16979  * Or you can initalize it using the {@link Roo.Element} object:
16980  <pre><code>
16981  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16982      startDrag : function(){
16983          this.constrainTo("parent-id");
16984      }
16985  });
16986  </code></pre>
16987      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16988      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16989      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16990      * an object containing the sides to pad. For example: {right:10, bottom:10}
16991      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16992      */
16993     constrainTo : function(constrainTo, pad, inContent){
16994         if(typeof pad == "number"){
16995             pad = {left: pad, right:pad, top:pad, bottom:pad};
16996         }
16997         pad = pad || this.defaultPadding;
16998         var b = Roo.get(this.getEl()).getBox();
16999         var ce = Roo.get(constrainTo);
17000         var s = ce.getScroll();
17001         var c, cd = ce.dom;
17002         if(cd == document.body){
17003             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
17004         }else{
17005             xy = ce.getXY();
17006             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
17007         }
17008
17009
17010         var topSpace = b.y - c.y;
17011         var leftSpace = b.x - c.x;
17012
17013         this.resetConstraints();
17014         this.setXConstraint(leftSpace - (pad.left||0), // left
17015                 c.width - leftSpace - b.width - (pad.right||0) //right
17016         );
17017         this.setYConstraint(topSpace - (pad.top||0), //top
17018                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
17019         );
17020     },
17021
17022     /**
17023      * Returns a reference to the linked element
17024      * @method getEl
17025      * @return {HTMLElement} the html element
17026      */
17027     getEl: function() {
17028         if (!this._domRef) {
17029             this._domRef = Roo.getDom(this.id);
17030         }
17031
17032         return this._domRef;
17033     },
17034
17035     /**
17036      * Returns a reference to the actual element to drag.  By default this is
17037      * the same as the html element, but it can be assigned to another
17038      * element. An example of this can be found in Roo.dd.DDProxy
17039      * @method getDragEl
17040      * @return {HTMLElement} the html element
17041      */
17042     getDragEl: function() {
17043         return Roo.getDom(this.dragElId);
17044     },
17045
17046     /**
17047      * Sets up the DragDrop object.  Must be called in the constructor of any
17048      * Roo.dd.DragDrop subclass
17049      * @method init
17050      * @param id the id of the linked element
17051      * @param {String} sGroup the group of related items
17052      * @param {object} config configuration attributes
17053      */
17054     init: function(id, sGroup, config) {
17055         this.initTarget(id, sGroup, config);
17056         if (!Roo.isTouch) {
17057             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17058         }
17059         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17060         // Event.on(this.id, "selectstart", Event.preventDefault);
17061     },
17062
17063     /**
17064      * Initializes Targeting functionality only... the object does not
17065      * get a mousedown handler.
17066      * @method initTarget
17067      * @param id the id of the linked element
17068      * @param {String} sGroup the group of related items
17069      * @param {object} config configuration attributes
17070      */
17071     initTarget: function(id, sGroup, config) {
17072
17073         // configuration attributes
17074         this.config = config || {};
17075
17076         // create a local reference to the drag and drop manager
17077         this.DDM = Roo.dd.DDM;
17078         // initialize the groups array
17079         this.groups = {};
17080
17081         // assume that we have an element reference instead of an id if the
17082         // parameter is not a string
17083         if (typeof id !== "string") {
17084             id = Roo.id(id);
17085         }
17086
17087         // set the id
17088         this.id = id;
17089
17090         // add to an interaction group
17091         this.addToGroup((sGroup) ? sGroup : "default");
17092
17093         // We don't want to register this as the handle with the manager
17094         // so we just set the id rather than calling the setter.
17095         this.handleElId = id;
17096
17097         // the linked element is the element that gets dragged by default
17098         this.setDragElId(id);
17099
17100         // by default, clicked anchors will not start drag operations.
17101         this.invalidHandleTypes = { A: "A" };
17102         this.invalidHandleIds = {};
17103         this.invalidHandleClasses = [];
17104
17105         this.applyConfig();
17106
17107         this.handleOnAvailable();
17108     },
17109
17110     /**
17111      * Applies the configuration parameters that were passed into the constructor.
17112      * This is supposed to happen at each level through the inheritance chain.  So
17113      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17114      * DragDrop in order to get all of the parameters that are available in
17115      * each object.
17116      * @method applyConfig
17117      */
17118     applyConfig: function() {
17119
17120         // configurable properties:
17121         //    padding, isTarget, maintainOffset, primaryButtonOnly
17122         this.padding           = this.config.padding || [0, 0, 0, 0];
17123         this.isTarget          = (this.config.isTarget !== false);
17124         this.maintainOffset    = (this.config.maintainOffset);
17125         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17126
17127     },
17128
17129     /**
17130      * Executed when the linked element is available
17131      * @method handleOnAvailable
17132      * @private
17133      */
17134     handleOnAvailable: function() {
17135         this.available = true;
17136         this.resetConstraints();
17137         this.onAvailable();
17138     },
17139
17140      /**
17141      * Configures the padding for the target zone in px.  Effectively expands
17142      * (or reduces) the virtual object size for targeting calculations.
17143      * Supports css-style shorthand; if only one parameter is passed, all sides
17144      * will have that padding, and if only two are passed, the top and bottom
17145      * will have the first param, the left and right the second.
17146      * @method setPadding
17147      * @param {int} iTop    Top pad
17148      * @param {int} iRight  Right pad
17149      * @param {int} iBot    Bot pad
17150      * @param {int} iLeft   Left pad
17151      */
17152     setPadding: function(iTop, iRight, iBot, iLeft) {
17153         // this.padding = [iLeft, iRight, iTop, iBot];
17154         if (!iRight && 0 !== iRight) {
17155             this.padding = [iTop, iTop, iTop, iTop];
17156         } else if (!iBot && 0 !== iBot) {
17157             this.padding = [iTop, iRight, iTop, iRight];
17158         } else {
17159             this.padding = [iTop, iRight, iBot, iLeft];
17160         }
17161     },
17162
17163     /**
17164      * Stores the initial placement of the linked element.
17165      * @method setInitialPosition
17166      * @param {int} diffX   the X offset, default 0
17167      * @param {int} diffY   the Y offset, default 0
17168      */
17169     setInitPosition: function(diffX, diffY) {
17170         var el = this.getEl();
17171
17172         if (!this.DDM.verifyEl(el)) {
17173             return;
17174         }
17175
17176         var dx = diffX || 0;
17177         var dy = diffY || 0;
17178
17179         var p = Dom.getXY( el );
17180
17181         this.initPageX = p[0] - dx;
17182         this.initPageY = p[1] - dy;
17183
17184         this.lastPageX = p[0];
17185         this.lastPageY = p[1];
17186
17187
17188         this.setStartPosition(p);
17189     },
17190
17191     /**
17192      * Sets the start position of the element.  This is set when the obj
17193      * is initialized, the reset when a drag is started.
17194      * @method setStartPosition
17195      * @param pos current position (from previous lookup)
17196      * @private
17197      */
17198     setStartPosition: function(pos) {
17199         var p = pos || Dom.getXY( this.getEl() );
17200         this.deltaSetXY = null;
17201
17202         this.startPageX = p[0];
17203         this.startPageY = p[1];
17204     },
17205
17206     /**
17207      * Add this instance to a group of related drag/drop objects.  All
17208      * instances belong to at least one group, and can belong to as many
17209      * groups as needed.
17210      * @method addToGroup
17211      * @param sGroup {string} the name of the group
17212      */
17213     addToGroup: function(sGroup) {
17214         this.groups[sGroup] = true;
17215         this.DDM.regDragDrop(this, sGroup);
17216     },
17217
17218     /**
17219      * Remove's this instance from the supplied interaction group
17220      * @method removeFromGroup
17221      * @param {string}  sGroup  The group to drop
17222      */
17223     removeFromGroup: function(sGroup) {
17224         if (this.groups[sGroup]) {
17225             delete this.groups[sGroup];
17226         }
17227
17228         this.DDM.removeDDFromGroup(this, sGroup);
17229     },
17230
17231     /**
17232      * Allows you to specify that an element other than the linked element
17233      * will be moved with the cursor during a drag
17234      * @method setDragElId
17235      * @param id {string} the id of the element that will be used to initiate the drag
17236      */
17237     setDragElId: function(id) {
17238         this.dragElId = id;
17239     },
17240
17241     /**
17242      * Allows you to specify a child of the linked element that should be
17243      * used to initiate the drag operation.  An example of this would be if
17244      * you have a content div with text and links.  Clicking anywhere in the
17245      * content area would normally start the drag operation.  Use this method
17246      * to specify that an element inside of the content div is the element
17247      * that starts the drag operation.
17248      * @method setHandleElId
17249      * @param id {string} the id of the element that will be used to
17250      * initiate the drag.
17251      */
17252     setHandleElId: function(id) {
17253         if (typeof id !== "string") {
17254             id = Roo.id(id);
17255         }
17256         this.handleElId = id;
17257         this.DDM.regHandle(this.id, id);
17258     },
17259
17260     /**
17261      * Allows you to set an element outside of the linked element as a drag
17262      * handle
17263      * @method setOuterHandleElId
17264      * @param id the id of the element that will be used to initiate the drag
17265      */
17266     setOuterHandleElId: function(id) {
17267         if (typeof id !== "string") {
17268             id = Roo.id(id);
17269         }
17270         Event.on(id, "mousedown",
17271                 this.handleMouseDown, this);
17272         this.setHandleElId(id);
17273
17274         this.hasOuterHandles = true;
17275     },
17276
17277     /**
17278      * Remove all drag and drop hooks for this element
17279      * @method unreg
17280      */
17281     unreg: function() {
17282         Event.un(this.id, "mousedown",
17283                 this.handleMouseDown);
17284         Event.un(this.id, "touchstart",
17285                 this.handleMouseDown);
17286         this._domRef = null;
17287         this.DDM._remove(this);
17288     },
17289
17290     destroy : function(){
17291         this.unreg();
17292     },
17293
17294     /**
17295      * Returns true if this instance is locked, or the drag drop mgr is locked
17296      * (meaning that all drag/drop is disabled on the page.)
17297      * @method isLocked
17298      * @return {boolean} true if this obj or all drag/drop is locked, else
17299      * false
17300      */
17301     isLocked: function() {
17302         return (this.DDM.isLocked() || this.locked);
17303     },
17304
17305     /**
17306      * Fired when this object is clicked
17307      * @method handleMouseDown
17308      * @param {Event} e
17309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17310      * @private
17311      */
17312     handleMouseDown: function(e, oDD){
17313      
17314         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17315             //Roo.log('not touch/ button !=0');
17316             return;
17317         }
17318         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17319             return; // double touch..
17320         }
17321         
17322
17323         if (this.isLocked()) {
17324             //Roo.log('locked');
17325             return;
17326         }
17327
17328         this.DDM.refreshCache(this.groups);
17329 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17330         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17331         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17332             //Roo.log('no outer handes or not over target');
17333                 // do nothing.
17334         } else {
17335 //            Roo.log('check validator');
17336             if (this.clickValidator(e)) {
17337 //                Roo.log('validate success');
17338                 // set the initial element position
17339                 this.setStartPosition();
17340
17341
17342                 this.b4MouseDown(e);
17343                 this.onMouseDown(e);
17344
17345                 this.DDM.handleMouseDown(e, this);
17346
17347                 this.DDM.stopEvent(e);
17348             } else {
17349
17350
17351             }
17352         }
17353     },
17354
17355     clickValidator: function(e) {
17356         var target = e.getTarget();
17357         return ( this.isValidHandleChild(target) &&
17358                     (this.id == this.handleElId ||
17359                         this.DDM.handleWasClicked(target, this.id)) );
17360     },
17361
17362     /**
17363      * Allows you to specify a tag name that should not start a drag operation
17364      * when clicked.  This is designed to facilitate embedding links within a
17365      * drag handle that do something other than start the drag.
17366      * @method addInvalidHandleType
17367      * @param {string} tagName the type of element to exclude
17368      */
17369     addInvalidHandleType: function(tagName) {
17370         var type = tagName.toUpperCase();
17371         this.invalidHandleTypes[type] = type;
17372     },
17373
17374     /**
17375      * Lets you to specify an element id for a child of a drag handle
17376      * that should not initiate a drag
17377      * @method addInvalidHandleId
17378      * @param {string} id the element id of the element you wish to ignore
17379      */
17380     addInvalidHandleId: function(id) {
17381         if (typeof id !== "string") {
17382             id = Roo.id(id);
17383         }
17384         this.invalidHandleIds[id] = id;
17385     },
17386
17387     /**
17388      * Lets you specify a css class of elements that will not initiate a drag
17389      * @method addInvalidHandleClass
17390      * @param {string} cssClass the class of the elements you wish to ignore
17391      */
17392     addInvalidHandleClass: function(cssClass) {
17393         this.invalidHandleClasses.push(cssClass);
17394     },
17395
17396     /**
17397      * Unsets an excluded tag name set by addInvalidHandleType
17398      * @method removeInvalidHandleType
17399      * @param {string} tagName the type of element to unexclude
17400      */
17401     removeInvalidHandleType: function(tagName) {
17402         var type = tagName.toUpperCase();
17403         // this.invalidHandleTypes[type] = null;
17404         delete this.invalidHandleTypes[type];
17405     },
17406
17407     /**
17408      * Unsets an invalid handle id
17409      * @method removeInvalidHandleId
17410      * @param {string} id the id of the element to re-enable
17411      */
17412     removeInvalidHandleId: function(id) {
17413         if (typeof id !== "string") {
17414             id = Roo.id(id);
17415         }
17416         delete this.invalidHandleIds[id];
17417     },
17418
17419     /**
17420      * Unsets an invalid css class
17421      * @method removeInvalidHandleClass
17422      * @param {string} cssClass the class of the element(s) you wish to
17423      * re-enable
17424      */
17425     removeInvalidHandleClass: function(cssClass) {
17426         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17427             if (this.invalidHandleClasses[i] == cssClass) {
17428                 delete this.invalidHandleClasses[i];
17429             }
17430         }
17431     },
17432
17433     /**
17434      * Checks the tag exclusion list to see if this click should be ignored
17435      * @method isValidHandleChild
17436      * @param {HTMLElement} node the HTMLElement to evaluate
17437      * @return {boolean} true if this is a valid tag type, false if not
17438      */
17439     isValidHandleChild: function(node) {
17440
17441         var valid = true;
17442         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17443         var nodeName;
17444         try {
17445             nodeName = node.nodeName.toUpperCase();
17446         } catch(e) {
17447             nodeName = node.nodeName;
17448         }
17449         valid = valid && !this.invalidHandleTypes[nodeName];
17450         valid = valid && !this.invalidHandleIds[node.id];
17451
17452         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17453             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17454         }
17455
17456
17457         return valid;
17458
17459     },
17460
17461     /**
17462      * Create the array of horizontal tick marks if an interval was specified
17463      * in setXConstraint().
17464      * @method setXTicks
17465      * @private
17466      */
17467     setXTicks: function(iStartX, iTickSize) {
17468         this.xTicks = [];
17469         this.xTickSize = iTickSize;
17470
17471         var tickMap = {};
17472
17473         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17474             if (!tickMap[i]) {
17475                 this.xTicks[this.xTicks.length] = i;
17476                 tickMap[i] = true;
17477             }
17478         }
17479
17480         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17481             if (!tickMap[i]) {
17482                 this.xTicks[this.xTicks.length] = i;
17483                 tickMap[i] = true;
17484             }
17485         }
17486
17487         this.xTicks.sort(this.DDM.numericSort) ;
17488     },
17489
17490     /**
17491      * Create the array of vertical tick marks if an interval was specified in
17492      * setYConstraint().
17493      * @method setYTicks
17494      * @private
17495      */
17496     setYTicks: function(iStartY, iTickSize) {
17497         this.yTicks = [];
17498         this.yTickSize = iTickSize;
17499
17500         var tickMap = {};
17501
17502         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17503             if (!tickMap[i]) {
17504                 this.yTicks[this.yTicks.length] = i;
17505                 tickMap[i] = true;
17506             }
17507         }
17508
17509         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17510             if (!tickMap[i]) {
17511                 this.yTicks[this.yTicks.length] = i;
17512                 tickMap[i] = true;
17513             }
17514         }
17515
17516         this.yTicks.sort(this.DDM.numericSort) ;
17517     },
17518
17519     /**
17520      * By default, the element can be dragged any place on the screen.  Use
17521      * this method to limit the horizontal travel of the element.  Pass in
17522      * 0,0 for the parameters if you want to lock the drag to the y axis.
17523      * @method setXConstraint
17524      * @param {int} iLeft the number of pixels the element can move to the left
17525      * @param {int} iRight the number of pixels the element can move to the
17526      * right
17527      * @param {int} iTickSize optional parameter for specifying that the
17528      * element
17529      * should move iTickSize pixels at a time.
17530      */
17531     setXConstraint: function(iLeft, iRight, iTickSize) {
17532         this.leftConstraint = iLeft;
17533         this.rightConstraint = iRight;
17534
17535         this.minX = this.initPageX - iLeft;
17536         this.maxX = this.initPageX + iRight;
17537         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17538
17539         this.constrainX = true;
17540     },
17541
17542     /**
17543      * Clears any constraints applied to this instance.  Also clears ticks
17544      * since they can't exist independent of a constraint at this time.
17545      * @method clearConstraints
17546      */
17547     clearConstraints: function() {
17548         this.constrainX = false;
17549         this.constrainY = false;
17550         this.clearTicks();
17551     },
17552
17553     /**
17554      * Clears any tick interval defined for this instance
17555      * @method clearTicks
17556      */
17557     clearTicks: function() {
17558         this.xTicks = null;
17559         this.yTicks = null;
17560         this.xTickSize = 0;
17561         this.yTickSize = 0;
17562     },
17563
17564     /**
17565      * By default, the element can be dragged any place on the screen.  Set
17566      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17567      * parameters if you want to lock the drag to the x axis.
17568      * @method setYConstraint
17569      * @param {int} iUp the number of pixels the element can move up
17570      * @param {int} iDown the number of pixels the element can move down
17571      * @param {int} iTickSize optional parameter for specifying that the
17572      * element should move iTickSize pixels at a time.
17573      */
17574     setYConstraint: function(iUp, iDown, iTickSize) {
17575         this.topConstraint = iUp;
17576         this.bottomConstraint = iDown;
17577
17578         this.minY = this.initPageY - iUp;
17579         this.maxY = this.initPageY + iDown;
17580         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17581
17582         this.constrainY = true;
17583
17584     },
17585
17586     /**
17587      * resetConstraints must be called if you manually reposition a dd element.
17588      * @method resetConstraints
17589      * @param {boolean} maintainOffset
17590      */
17591     resetConstraints: function() {
17592
17593
17594         // Maintain offsets if necessary
17595         if (this.initPageX || this.initPageX === 0) {
17596             // figure out how much this thing has moved
17597             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17598             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17599
17600             this.setInitPosition(dx, dy);
17601
17602         // This is the first time we have detected the element's position
17603         } else {
17604             this.setInitPosition();
17605         }
17606
17607         if (this.constrainX) {
17608             this.setXConstraint( this.leftConstraint,
17609                                  this.rightConstraint,
17610                                  this.xTickSize        );
17611         }
17612
17613         if (this.constrainY) {
17614             this.setYConstraint( this.topConstraint,
17615                                  this.bottomConstraint,
17616                                  this.yTickSize         );
17617         }
17618     },
17619
17620     /**
17621      * Normally the drag element is moved pixel by pixel, but we can specify
17622      * that it move a number of pixels at a time.  This method resolves the
17623      * location when we have it set up like this.
17624      * @method getTick
17625      * @param {int} val where we want to place the object
17626      * @param {int[]} tickArray sorted array of valid points
17627      * @return {int} the closest tick
17628      * @private
17629      */
17630     getTick: function(val, tickArray) {
17631
17632         if (!tickArray) {
17633             // If tick interval is not defined, it is effectively 1 pixel,
17634             // so we return the value passed to us.
17635             return val;
17636         } else if (tickArray[0] >= val) {
17637             // The value is lower than the first tick, so we return the first
17638             // tick.
17639             return tickArray[0];
17640         } else {
17641             for (var i=0, len=tickArray.length; i<len; ++i) {
17642                 var next = i + 1;
17643                 if (tickArray[next] && tickArray[next] >= val) {
17644                     var diff1 = val - tickArray[i];
17645                     var diff2 = tickArray[next] - val;
17646                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17647                 }
17648             }
17649
17650             // The value is larger than the last tick, so we return the last
17651             // tick.
17652             return tickArray[tickArray.length - 1];
17653         }
17654     },
17655
17656     /**
17657      * toString method
17658      * @method toString
17659      * @return {string} string representation of the dd obj
17660      */
17661     toString: function() {
17662         return ("DragDrop " + this.id);
17663     }
17664
17665 });
17666
17667 })();
17668 /*
17669  * Based on:
17670  * Ext JS Library 1.1.1
17671  * Copyright(c) 2006-2007, Ext JS, LLC.
17672  *
17673  * Originally Released Under LGPL - original licence link has changed is not relivant.
17674  *
17675  * Fork - LGPL
17676  * <script type="text/javascript">
17677  */
17678
17679
17680 /**
17681  * The drag and drop utility provides a framework for building drag and drop
17682  * applications.  In addition to enabling drag and drop for specific elements,
17683  * the drag and drop elements are tracked by the manager class, and the
17684  * interactions between the various elements are tracked during the drag and
17685  * the implementing code is notified about these important moments.
17686  */
17687
17688 // Only load the library once.  Rewriting the manager class would orphan
17689 // existing drag and drop instances.
17690 if (!Roo.dd.DragDropMgr) {
17691
17692 /**
17693  * @class Roo.dd.DragDropMgr
17694  * DragDropMgr is a singleton that tracks the element interaction for
17695  * all DragDrop items in the window.  Generally, you will not call
17696  * this class directly, but it does have helper methods that could
17697  * be useful in your DragDrop implementations.
17698  * @singleton
17699  */
17700 Roo.dd.DragDropMgr = function() {
17701
17702     var Event = Roo.EventManager;
17703
17704     return {
17705
17706         /**
17707          * Two dimensional Array of registered DragDrop objects.  The first
17708          * dimension is the DragDrop item group, the second the DragDrop
17709          * object.
17710          * @property ids
17711          * @type {string: string}
17712          * @private
17713          * @static
17714          */
17715         ids: {},
17716
17717         /**
17718          * Array of element ids defined as drag handles.  Used to determine
17719          * if the element that generated the mousedown event is actually the
17720          * handle and not the html element itself.
17721          * @property handleIds
17722          * @type {string: string}
17723          * @private
17724          * @static
17725          */
17726         handleIds: {},
17727
17728         /**
17729          * the DragDrop object that is currently being dragged
17730          * @property dragCurrent
17731          * @type DragDrop
17732          * @private
17733          * @static
17734          **/
17735         dragCurrent: null,
17736
17737         /**
17738          * the DragDrop object(s) that are being hovered over
17739          * @property dragOvers
17740          * @type Array
17741          * @private
17742          * @static
17743          */
17744         dragOvers: {},
17745
17746         /**
17747          * the X distance between the cursor and the object being dragged
17748          * @property deltaX
17749          * @type int
17750          * @private
17751          * @static
17752          */
17753         deltaX: 0,
17754
17755         /**
17756          * the Y distance between the cursor and the object being dragged
17757          * @property deltaY
17758          * @type int
17759          * @private
17760          * @static
17761          */
17762         deltaY: 0,
17763
17764         /**
17765          * Flag to determine if we should prevent the default behavior of the
17766          * events we define. By default this is true, but this can be set to
17767          * false if you need the default behavior (not recommended)
17768          * @property preventDefault
17769          * @type boolean
17770          * @static
17771          */
17772         preventDefault: true,
17773
17774         /**
17775          * Flag to determine if we should stop the propagation of the events
17776          * we generate. This is true by default but you may want to set it to
17777          * false if the html element contains other features that require the
17778          * mouse click.
17779          * @property stopPropagation
17780          * @type boolean
17781          * @static
17782          */
17783         stopPropagation: true,
17784
17785         /**
17786          * Internal flag that is set to true when drag and drop has been
17787          * intialized
17788          * @property initialized
17789          * @private
17790          * @static
17791          */
17792         initalized: false,
17793
17794         /**
17795          * All drag and drop can be disabled.
17796          * @property locked
17797          * @private
17798          * @static
17799          */
17800         locked: false,
17801
17802         /**
17803          * Called the first time an element is registered.
17804          * @method init
17805          * @private
17806          * @static
17807          */
17808         init: function() {
17809             this.initialized = true;
17810         },
17811
17812         /**
17813          * In point mode, drag and drop interaction is defined by the
17814          * location of the cursor during the drag/drop
17815          * @property POINT
17816          * @type int
17817          * @static
17818          */
17819         POINT: 0,
17820
17821         /**
17822          * In intersect mode, drag and drop interactio nis defined by the
17823          * overlap of two or more drag and drop objects.
17824          * @property INTERSECT
17825          * @type int
17826          * @static
17827          */
17828         INTERSECT: 1,
17829
17830         /**
17831          * The current drag and drop mode.  Default: POINT
17832          * @property mode
17833          * @type int
17834          * @static
17835          */
17836         mode: 0,
17837
17838         /**
17839          * Runs method on all drag and drop objects
17840          * @method _execOnAll
17841          * @private
17842          * @static
17843          */
17844         _execOnAll: function(sMethod, args) {
17845             for (var i in this.ids) {
17846                 for (var j in this.ids[i]) {
17847                     var oDD = this.ids[i][j];
17848                     if (! this.isTypeOfDD(oDD)) {
17849                         continue;
17850                     }
17851                     oDD[sMethod].apply(oDD, args);
17852                 }
17853             }
17854         },
17855
17856         /**
17857          * Drag and drop initialization.  Sets up the global event handlers
17858          * @method _onLoad
17859          * @private
17860          * @static
17861          */
17862         _onLoad: function() {
17863
17864             this.init();
17865
17866             if (!Roo.isTouch) {
17867                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17868                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17869             }
17870             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17871             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17872             
17873             Event.on(window,   "unload",    this._onUnload, this, true);
17874             Event.on(window,   "resize",    this._onResize, this, true);
17875             // Event.on(window,   "mouseout",    this._test);
17876
17877         },
17878
17879         /**
17880          * Reset constraints on all drag and drop objs
17881          * @method _onResize
17882          * @private
17883          * @static
17884          */
17885         _onResize: function(e) {
17886             this._execOnAll("resetConstraints", []);
17887         },
17888
17889         /**
17890          * Lock all drag and drop functionality
17891          * @method lock
17892          * @static
17893          */
17894         lock: function() { this.locked = true; },
17895
17896         /**
17897          * Unlock all drag and drop functionality
17898          * @method unlock
17899          * @static
17900          */
17901         unlock: function() { this.locked = false; },
17902
17903         /**
17904          * Is drag and drop locked?
17905          * @method isLocked
17906          * @return {boolean} True if drag and drop is locked, false otherwise.
17907          * @static
17908          */
17909         isLocked: function() { return this.locked; },
17910
17911         /**
17912          * Location cache that is set for all drag drop objects when a drag is
17913          * initiated, cleared when the drag is finished.
17914          * @property locationCache
17915          * @private
17916          * @static
17917          */
17918         locationCache: {},
17919
17920         /**
17921          * Set useCache to false if you want to force object the lookup of each
17922          * drag and drop linked element constantly during a drag.
17923          * @property useCache
17924          * @type boolean
17925          * @static
17926          */
17927         useCache: true,
17928
17929         /**
17930          * The number of pixels that the mouse needs to move after the
17931          * mousedown before the drag is initiated.  Default=3;
17932          * @property clickPixelThresh
17933          * @type int
17934          * @static
17935          */
17936         clickPixelThresh: 3,
17937
17938         /**
17939          * The number of milliseconds after the mousedown event to initiate the
17940          * drag if we don't get a mouseup event. Default=1000
17941          * @property clickTimeThresh
17942          * @type int
17943          * @static
17944          */
17945         clickTimeThresh: 350,
17946
17947         /**
17948          * Flag that indicates that either the drag pixel threshold or the
17949          * mousdown time threshold has been met
17950          * @property dragThreshMet
17951          * @type boolean
17952          * @private
17953          * @static
17954          */
17955         dragThreshMet: false,
17956
17957         /**
17958          * Timeout used for the click time threshold
17959          * @property clickTimeout
17960          * @type Object
17961          * @private
17962          * @static
17963          */
17964         clickTimeout: null,
17965
17966         /**
17967          * The X position of the mousedown event stored for later use when a
17968          * drag threshold is met.
17969          * @property startX
17970          * @type int
17971          * @private
17972          * @static
17973          */
17974         startX: 0,
17975
17976         /**
17977          * The Y position of the mousedown event stored for later use when a
17978          * drag threshold is met.
17979          * @property startY
17980          * @type int
17981          * @private
17982          * @static
17983          */
17984         startY: 0,
17985
17986         /**
17987          * Each DragDrop instance must be registered with the DragDropMgr.
17988          * This is executed in DragDrop.init()
17989          * @method regDragDrop
17990          * @param {DragDrop} oDD the DragDrop object to register
17991          * @param {String} sGroup the name of the group this element belongs to
17992          * @static
17993          */
17994         regDragDrop: function(oDD, sGroup) {
17995             if (!this.initialized) { this.init(); }
17996
17997             if (!this.ids[sGroup]) {
17998                 this.ids[sGroup] = {};
17999             }
18000             this.ids[sGroup][oDD.id] = oDD;
18001         },
18002
18003         /**
18004          * Removes the supplied dd instance from the supplied group. Executed
18005          * by DragDrop.removeFromGroup, so don't call this function directly.
18006          * @method removeDDFromGroup
18007          * @private
18008          * @static
18009          */
18010         removeDDFromGroup: function(oDD, sGroup) {
18011             if (!this.ids[sGroup]) {
18012                 this.ids[sGroup] = {};
18013             }
18014
18015             var obj = this.ids[sGroup];
18016             if (obj && obj[oDD.id]) {
18017                 delete obj[oDD.id];
18018             }
18019         },
18020
18021         /**
18022          * Unregisters a drag and drop item.  This is executed in
18023          * DragDrop.unreg, use that method instead of calling this directly.
18024          * @method _remove
18025          * @private
18026          * @static
18027          */
18028         _remove: function(oDD) {
18029             for (var g in oDD.groups) {
18030                 if (g && this.ids[g][oDD.id]) {
18031                     delete this.ids[g][oDD.id];
18032                 }
18033             }
18034             delete this.handleIds[oDD.id];
18035         },
18036
18037         /**
18038          * Each DragDrop handle element must be registered.  This is done
18039          * automatically when executing DragDrop.setHandleElId()
18040          * @method regHandle
18041          * @param {String} sDDId the DragDrop id this element is a handle for
18042          * @param {String} sHandleId the id of the element that is the drag
18043          * handle
18044          * @static
18045          */
18046         regHandle: function(sDDId, sHandleId) {
18047             if (!this.handleIds[sDDId]) {
18048                 this.handleIds[sDDId] = {};
18049             }
18050             this.handleIds[sDDId][sHandleId] = sHandleId;
18051         },
18052
18053         /**
18054          * Utility function to determine if a given element has been
18055          * registered as a drag drop item.
18056          * @method isDragDrop
18057          * @param {String} id the element id to check
18058          * @return {boolean} true if this element is a DragDrop item,
18059          * false otherwise
18060          * @static
18061          */
18062         isDragDrop: function(id) {
18063             return ( this.getDDById(id) ) ? true : false;
18064         },
18065
18066         /**
18067          * Returns the drag and drop instances that are in all groups the
18068          * passed in instance belongs to.
18069          * @method getRelated
18070          * @param {DragDrop} p_oDD the obj to get related data for
18071          * @param {boolean} bTargetsOnly if true, only return targetable objs
18072          * @return {DragDrop[]} the related instances
18073          * @static
18074          */
18075         getRelated: function(p_oDD, bTargetsOnly) {
18076             var oDDs = [];
18077             for (var i in p_oDD.groups) {
18078                 for (j in this.ids[i]) {
18079                     var dd = this.ids[i][j];
18080                     if (! this.isTypeOfDD(dd)) {
18081                         continue;
18082                     }
18083                     if (!bTargetsOnly || dd.isTarget) {
18084                         oDDs[oDDs.length] = dd;
18085                     }
18086                 }
18087             }
18088
18089             return oDDs;
18090         },
18091
18092         /**
18093          * Returns true if the specified dd target is a legal target for
18094          * the specifice drag obj
18095          * @method isLegalTarget
18096          * @param {DragDrop} the drag obj
18097          * @param {DragDrop} the target
18098          * @return {boolean} true if the target is a legal target for the
18099          * dd obj
18100          * @static
18101          */
18102         isLegalTarget: function (oDD, oTargetDD) {
18103             var targets = this.getRelated(oDD, true);
18104             for (var i=0, len=targets.length;i<len;++i) {
18105                 if (targets[i].id == oTargetDD.id) {
18106                     return true;
18107                 }
18108             }
18109
18110             return false;
18111         },
18112
18113         /**
18114          * My goal is to be able to transparently determine if an object is
18115          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18116          * returns "object", oDD.constructor.toString() always returns
18117          * "DragDrop" and not the name of the subclass.  So for now it just
18118          * evaluates a well-known variable in DragDrop.
18119          * @method isTypeOfDD
18120          * @param {Object} the object to evaluate
18121          * @return {boolean} true if typeof oDD = DragDrop
18122          * @static
18123          */
18124         isTypeOfDD: function (oDD) {
18125             return (oDD && oDD.__ygDragDrop);
18126         },
18127
18128         /**
18129          * Utility function to determine if a given element has been
18130          * registered as a drag drop handle for the given Drag Drop object.
18131          * @method isHandle
18132          * @param {String} id the element id to check
18133          * @return {boolean} true if this element is a DragDrop handle, false
18134          * otherwise
18135          * @static
18136          */
18137         isHandle: function(sDDId, sHandleId) {
18138             return ( this.handleIds[sDDId] &&
18139                             this.handleIds[sDDId][sHandleId] );
18140         },
18141
18142         /**
18143          * Returns the DragDrop instance for a given id
18144          * @method getDDById
18145          * @param {String} id the id of the DragDrop object
18146          * @return {DragDrop} the drag drop object, null if it is not found
18147          * @static
18148          */
18149         getDDById: function(id) {
18150             for (var i in this.ids) {
18151                 if (this.ids[i][id]) {
18152                     return this.ids[i][id];
18153                 }
18154             }
18155             return null;
18156         },
18157
18158         /**
18159          * Fired after a registered DragDrop object gets the mousedown event.
18160          * Sets up the events required to track the object being dragged
18161          * @method handleMouseDown
18162          * @param {Event} e the event
18163          * @param oDD the DragDrop object being dragged
18164          * @private
18165          * @static
18166          */
18167         handleMouseDown: function(e, oDD) {
18168             if(Roo.QuickTips){
18169                 Roo.QuickTips.disable();
18170             }
18171             this.currentTarget = e.getTarget();
18172
18173             this.dragCurrent = oDD;
18174
18175             var el = oDD.getEl();
18176
18177             // track start position
18178             this.startX = e.getPageX();
18179             this.startY = e.getPageY();
18180
18181             this.deltaX = this.startX - el.offsetLeft;
18182             this.deltaY = this.startY - el.offsetTop;
18183
18184             this.dragThreshMet = false;
18185
18186             this.clickTimeout = setTimeout(
18187                     function() {
18188                         var DDM = Roo.dd.DDM;
18189                         DDM.startDrag(DDM.startX, DDM.startY);
18190                     },
18191                     this.clickTimeThresh );
18192         },
18193
18194         /**
18195          * Fired when either the drag pixel threshol or the mousedown hold
18196          * time threshold has been met.
18197          * @method startDrag
18198          * @param x {int} the X position of the original mousedown
18199          * @param y {int} the Y position of the original mousedown
18200          * @static
18201          */
18202         startDrag: function(x, y) {
18203             clearTimeout(this.clickTimeout);
18204             if (this.dragCurrent) {
18205                 this.dragCurrent.b4StartDrag(x, y);
18206                 this.dragCurrent.startDrag(x, y);
18207             }
18208             this.dragThreshMet = true;
18209         },
18210
18211         /**
18212          * Internal function to handle the mouseup event.  Will be invoked
18213          * from the context of the document.
18214          * @method handleMouseUp
18215          * @param {Event} e the event
18216          * @private
18217          * @static
18218          */
18219         handleMouseUp: function(e) {
18220
18221             if(Roo.QuickTips){
18222                 Roo.QuickTips.enable();
18223             }
18224             if (! this.dragCurrent) {
18225                 return;
18226             }
18227
18228             clearTimeout(this.clickTimeout);
18229
18230             if (this.dragThreshMet) {
18231                 this.fireEvents(e, true);
18232             } else {
18233             }
18234
18235             this.stopDrag(e);
18236
18237             this.stopEvent(e);
18238         },
18239
18240         /**
18241          * Utility to stop event propagation and event default, if these
18242          * features are turned on.
18243          * @method stopEvent
18244          * @param {Event} e the event as returned by this.getEvent()
18245          * @static
18246          */
18247         stopEvent: function(e){
18248             if(this.stopPropagation) {
18249                 e.stopPropagation();
18250             }
18251
18252             if (this.preventDefault) {
18253                 e.preventDefault();
18254             }
18255         },
18256
18257         /**
18258          * Internal function to clean up event handlers after the drag
18259          * operation is complete
18260          * @method stopDrag
18261          * @param {Event} e the event
18262          * @private
18263          * @static
18264          */
18265         stopDrag: function(e) {
18266             // Fire the drag end event for the item that was dragged
18267             if (this.dragCurrent) {
18268                 if (this.dragThreshMet) {
18269                     this.dragCurrent.b4EndDrag(e);
18270                     this.dragCurrent.endDrag(e);
18271                 }
18272
18273                 this.dragCurrent.onMouseUp(e);
18274             }
18275
18276             this.dragCurrent = null;
18277             this.dragOvers = {};
18278         },
18279
18280         /**
18281          * Internal function to handle the mousemove event.  Will be invoked
18282          * from the context of the html element.
18283          *
18284          * @TODO figure out what we can do about mouse events lost when the
18285          * user drags objects beyond the window boundary.  Currently we can
18286          * detect this in internet explorer by verifying that the mouse is
18287          * down during the mousemove event.  Firefox doesn't give us the
18288          * button state on the mousemove event.
18289          * @method handleMouseMove
18290          * @param {Event} e the event
18291          * @private
18292          * @static
18293          */
18294         handleMouseMove: function(e) {
18295             if (! this.dragCurrent) {
18296                 return true;
18297             }
18298
18299             // var button = e.which || e.button;
18300
18301             // check for IE mouseup outside of page boundary
18302             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18303                 this.stopEvent(e);
18304                 return this.handleMouseUp(e);
18305             }
18306
18307             if (!this.dragThreshMet) {
18308                 var diffX = Math.abs(this.startX - e.getPageX());
18309                 var diffY = Math.abs(this.startY - e.getPageY());
18310                 if (diffX > this.clickPixelThresh ||
18311                             diffY > this.clickPixelThresh) {
18312                     this.startDrag(this.startX, this.startY);
18313                 }
18314             }
18315
18316             if (this.dragThreshMet) {
18317                 this.dragCurrent.b4Drag(e);
18318                 this.dragCurrent.onDrag(e);
18319                 if(!this.dragCurrent.moveOnly){
18320                     this.fireEvents(e, false);
18321                 }
18322             }
18323
18324             this.stopEvent(e);
18325
18326             return true;
18327         },
18328
18329         /**
18330          * Iterates over all of the DragDrop elements to find ones we are
18331          * hovering over or dropping on
18332          * @method fireEvents
18333          * @param {Event} e the event
18334          * @param {boolean} isDrop is this a drop op or a mouseover op?
18335          * @private
18336          * @static
18337          */
18338         fireEvents: function(e, isDrop) {
18339             var dc = this.dragCurrent;
18340
18341             // If the user did the mouse up outside of the window, we could
18342             // get here even though we have ended the drag.
18343             if (!dc || dc.isLocked()) {
18344                 return;
18345             }
18346
18347             var pt = e.getPoint();
18348
18349             // cache the previous dragOver array
18350             var oldOvers = [];
18351
18352             var outEvts   = [];
18353             var overEvts  = [];
18354             var dropEvts  = [];
18355             var enterEvts = [];
18356
18357             // Check to see if the object(s) we were hovering over is no longer
18358             // being hovered over so we can fire the onDragOut event
18359             for (var i in this.dragOvers) {
18360
18361                 var ddo = this.dragOvers[i];
18362
18363                 if (! this.isTypeOfDD(ddo)) {
18364                     continue;
18365                 }
18366
18367                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18368                     outEvts.push( ddo );
18369                 }
18370
18371                 oldOvers[i] = true;
18372                 delete this.dragOvers[i];
18373             }
18374
18375             for (var sGroup in dc.groups) {
18376
18377                 if ("string" != typeof sGroup) {
18378                     continue;
18379                 }
18380
18381                 for (i in this.ids[sGroup]) {
18382                     var oDD = this.ids[sGroup][i];
18383                     if (! this.isTypeOfDD(oDD)) {
18384                         continue;
18385                     }
18386
18387                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18388                         if (this.isOverTarget(pt, oDD, this.mode)) {
18389                             // look for drop interactions
18390                             if (isDrop) {
18391                                 dropEvts.push( oDD );
18392                             // look for drag enter and drag over interactions
18393                             } else {
18394
18395                                 // initial drag over: dragEnter fires
18396                                 if (!oldOvers[oDD.id]) {
18397                                     enterEvts.push( oDD );
18398                                 // subsequent drag overs: dragOver fires
18399                                 } else {
18400                                     overEvts.push( oDD );
18401                                 }
18402
18403                                 this.dragOvers[oDD.id] = oDD;
18404                             }
18405                         }
18406                     }
18407                 }
18408             }
18409
18410             if (this.mode) {
18411                 if (outEvts.length) {
18412                     dc.b4DragOut(e, outEvts);
18413                     dc.onDragOut(e, outEvts);
18414                 }
18415
18416                 if (enterEvts.length) {
18417                     dc.onDragEnter(e, enterEvts);
18418                 }
18419
18420                 if (overEvts.length) {
18421                     dc.b4DragOver(e, overEvts);
18422                     dc.onDragOver(e, overEvts);
18423                 }
18424
18425                 if (dropEvts.length) {
18426                     dc.b4DragDrop(e, dropEvts);
18427                     dc.onDragDrop(e, dropEvts);
18428                 }
18429
18430             } else {
18431                 // fire dragout events
18432                 var len = 0;
18433                 for (i=0, len=outEvts.length; i<len; ++i) {
18434                     dc.b4DragOut(e, outEvts[i].id);
18435                     dc.onDragOut(e, outEvts[i].id);
18436                 }
18437
18438                 // fire enter events
18439                 for (i=0,len=enterEvts.length; i<len; ++i) {
18440                     // dc.b4DragEnter(e, oDD.id);
18441                     dc.onDragEnter(e, enterEvts[i].id);
18442                 }
18443
18444                 // fire over events
18445                 for (i=0,len=overEvts.length; i<len; ++i) {
18446                     dc.b4DragOver(e, overEvts[i].id);
18447                     dc.onDragOver(e, overEvts[i].id);
18448                 }
18449
18450                 // fire drop events
18451                 for (i=0, len=dropEvts.length; i<len; ++i) {
18452                     dc.b4DragDrop(e, dropEvts[i].id);
18453                     dc.onDragDrop(e, dropEvts[i].id);
18454                 }
18455
18456             }
18457
18458             // notify about a drop that did not find a target
18459             if (isDrop && !dropEvts.length) {
18460                 dc.onInvalidDrop(e);
18461             }
18462
18463         },
18464
18465         /**
18466          * Helper function for getting the best match from the list of drag
18467          * and drop objects returned by the drag and drop events when we are
18468          * in INTERSECT mode.  It returns either the first object that the
18469          * cursor is over, or the object that has the greatest overlap with
18470          * the dragged element.
18471          * @method getBestMatch
18472          * @param  {DragDrop[]} dds The array of drag and drop objects
18473          * targeted
18474          * @return {DragDrop}       The best single match
18475          * @static
18476          */
18477         getBestMatch: function(dds) {
18478             var winner = null;
18479             // Return null if the input is not what we expect
18480             //if (!dds || !dds.length || dds.length == 0) {
18481                // winner = null;
18482             // If there is only one item, it wins
18483             //} else if (dds.length == 1) {
18484
18485             var len = dds.length;
18486
18487             if (len == 1) {
18488                 winner = dds[0];
18489             } else {
18490                 // Loop through the targeted items
18491                 for (var i=0; i<len; ++i) {
18492                     var dd = dds[i];
18493                     // If the cursor is over the object, it wins.  If the
18494                     // cursor is over multiple matches, the first one we come
18495                     // to wins.
18496                     if (dd.cursorIsOver) {
18497                         winner = dd;
18498                         break;
18499                     // Otherwise the object with the most overlap wins
18500                     } else {
18501                         if (!winner ||
18502                             winner.overlap.getArea() < dd.overlap.getArea()) {
18503                             winner = dd;
18504                         }
18505                     }
18506                 }
18507             }
18508
18509             return winner;
18510         },
18511
18512         /**
18513          * Refreshes the cache of the top-left and bottom-right points of the
18514          * drag and drop objects in the specified group(s).  This is in the
18515          * format that is stored in the drag and drop instance, so typical
18516          * usage is:
18517          * <code>
18518          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18519          * </code>
18520          * Alternatively:
18521          * <code>
18522          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18523          * </code>
18524          * @TODO this really should be an indexed array.  Alternatively this
18525          * method could accept both.
18526          * @method refreshCache
18527          * @param {Object} groups an associative array of groups to refresh
18528          * @static
18529          */
18530         refreshCache: function(groups) {
18531             for (var sGroup in groups) {
18532                 if ("string" != typeof sGroup) {
18533                     continue;
18534                 }
18535                 for (var i in this.ids[sGroup]) {
18536                     var oDD = this.ids[sGroup][i];
18537
18538                     if (this.isTypeOfDD(oDD)) {
18539                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18540                         var loc = this.getLocation(oDD);
18541                         if (loc) {
18542                             this.locationCache[oDD.id] = loc;
18543                         } else {
18544                             delete this.locationCache[oDD.id];
18545                             // this will unregister the drag and drop object if
18546                             // the element is not in a usable state
18547                             // oDD.unreg();
18548                         }
18549                     }
18550                 }
18551             }
18552         },
18553
18554         /**
18555          * This checks to make sure an element exists and is in the DOM.  The
18556          * main purpose is to handle cases where innerHTML is used to remove
18557          * drag and drop objects from the DOM.  IE provides an 'unspecified
18558          * error' when trying to access the offsetParent of such an element
18559          * @method verifyEl
18560          * @param {HTMLElement} el the element to check
18561          * @return {boolean} true if the element looks usable
18562          * @static
18563          */
18564         verifyEl: function(el) {
18565             if (el) {
18566                 var parent;
18567                 if(Roo.isIE){
18568                     try{
18569                         parent = el.offsetParent;
18570                     }catch(e){}
18571                 }else{
18572                     parent = el.offsetParent;
18573                 }
18574                 if (parent) {
18575                     return true;
18576                 }
18577             }
18578
18579             return false;
18580         },
18581
18582         /**
18583          * Returns a Region object containing the drag and drop element's position
18584          * and size, including the padding configured for it
18585          * @method getLocation
18586          * @param {DragDrop} oDD the drag and drop object to get the
18587          *                       location for
18588          * @return {Roo.lib.Region} a Region object representing the total area
18589          *                             the element occupies, including any padding
18590          *                             the instance is configured for.
18591          * @static
18592          */
18593         getLocation: function(oDD) {
18594             if (! this.isTypeOfDD(oDD)) {
18595                 return null;
18596             }
18597
18598             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18599
18600             try {
18601                 pos= Roo.lib.Dom.getXY(el);
18602             } catch (e) { }
18603
18604             if (!pos) {
18605                 return null;
18606             }
18607
18608             x1 = pos[0];
18609             x2 = x1 + el.offsetWidth;
18610             y1 = pos[1];
18611             y2 = y1 + el.offsetHeight;
18612
18613             t = y1 - oDD.padding[0];
18614             r = x2 + oDD.padding[1];
18615             b = y2 + oDD.padding[2];
18616             l = x1 - oDD.padding[3];
18617
18618             return new Roo.lib.Region( t, r, b, l );
18619         },
18620
18621         /**
18622          * Checks the cursor location to see if it over the target
18623          * @method isOverTarget
18624          * @param {Roo.lib.Point} pt The point to evaluate
18625          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18626          * @return {boolean} true if the mouse is over the target
18627          * @private
18628          * @static
18629          */
18630         isOverTarget: function(pt, oTarget, intersect) {
18631             // use cache if available
18632             var loc = this.locationCache[oTarget.id];
18633             if (!loc || !this.useCache) {
18634                 loc = this.getLocation(oTarget);
18635                 this.locationCache[oTarget.id] = loc;
18636
18637             }
18638
18639             if (!loc) {
18640                 return false;
18641             }
18642
18643             oTarget.cursorIsOver = loc.contains( pt );
18644
18645             // DragDrop is using this as a sanity check for the initial mousedown
18646             // in this case we are done.  In POINT mode, if the drag obj has no
18647             // contraints, we are also done. Otherwise we need to evaluate the
18648             // location of the target as related to the actual location of the
18649             // dragged element.
18650             var dc = this.dragCurrent;
18651             if (!dc || !dc.getTargetCoord ||
18652                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18653                 return oTarget.cursorIsOver;
18654             }
18655
18656             oTarget.overlap = null;
18657
18658             // Get the current location of the drag element, this is the
18659             // location of the mouse event less the delta that represents
18660             // where the original mousedown happened on the element.  We
18661             // need to consider constraints and ticks as well.
18662             var pos = dc.getTargetCoord(pt.x, pt.y);
18663
18664             var el = dc.getDragEl();
18665             var curRegion = new Roo.lib.Region( pos.y,
18666                                                    pos.x + el.offsetWidth,
18667                                                    pos.y + el.offsetHeight,
18668                                                    pos.x );
18669
18670             var overlap = curRegion.intersect(loc);
18671
18672             if (overlap) {
18673                 oTarget.overlap = overlap;
18674                 return (intersect) ? true : oTarget.cursorIsOver;
18675             } else {
18676                 return false;
18677             }
18678         },
18679
18680         /**
18681          * unload event handler
18682          * @method _onUnload
18683          * @private
18684          * @static
18685          */
18686         _onUnload: function(e, me) {
18687             Roo.dd.DragDropMgr.unregAll();
18688         },
18689
18690         /**
18691          * Cleans up the drag and drop events and objects.
18692          * @method unregAll
18693          * @private
18694          * @static
18695          */
18696         unregAll: function() {
18697
18698             if (this.dragCurrent) {
18699                 this.stopDrag();
18700                 this.dragCurrent = null;
18701             }
18702
18703             this._execOnAll("unreg", []);
18704
18705             for (i in this.elementCache) {
18706                 delete this.elementCache[i];
18707             }
18708
18709             this.elementCache = {};
18710             this.ids = {};
18711         },
18712
18713         /**
18714          * A cache of DOM elements
18715          * @property elementCache
18716          * @private
18717          * @static
18718          */
18719         elementCache: {},
18720
18721         /**
18722          * Get the wrapper for the DOM element specified
18723          * @method getElWrapper
18724          * @param {String} id the id of the element to get
18725          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18726          * @private
18727          * @deprecated This wrapper isn't that useful
18728          * @static
18729          */
18730         getElWrapper: function(id) {
18731             var oWrapper = this.elementCache[id];
18732             if (!oWrapper || !oWrapper.el) {
18733                 oWrapper = this.elementCache[id] =
18734                     new this.ElementWrapper(Roo.getDom(id));
18735             }
18736             return oWrapper;
18737         },
18738
18739         /**
18740          * Returns the actual DOM element
18741          * @method getElement
18742          * @param {String} id the id of the elment to get
18743          * @return {Object} The element
18744          * @deprecated use Roo.getDom instead
18745          * @static
18746          */
18747         getElement: function(id) {
18748             return Roo.getDom(id);
18749         },
18750
18751         /**
18752          * Returns the style property for the DOM element (i.e.,
18753          * document.getElById(id).style)
18754          * @method getCss
18755          * @param {String} id the id of the elment to get
18756          * @return {Object} The style property of the element
18757          * @deprecated use Roo.getDom instead
18758          * @static
18759          */
18760         getCss: function(id) {
18761             var el = Roo.getDom(id);
18762             return (el) ? el.style : null;
18763         },
18764
18765         /**
18766          * Inner class for cached elements
18767          * @class DragDropMgr.ElementWrapper
18768          * @for DragDropMgr
18769          * @private
18770          * @deprecated
18771          */
18772         ElementWrapper: function(el) {
18773                 /**
18774                  * The element
18775                  * @property el
18776                  */
18777                 this.el = el || null;
18778                 /**
18779                  * The element id
18780                  * @property id
18781                  */
18782                 this.id = this.el && el.id;
18783                 /**
18784                  * A reference to the style property
18785                  * @property css
18786                  */
18787                 this.css = this.el && el.style;
18788             },
18789
18790         /**
18791          * Returns the X position of an html element
18792          * @method getPosX
18793          * @param el the element for which to get the position
18794          * @return {int} the X coordinate
18795          * @for DragDropMgr
18796          * @deprecated use Roo.lib.Dom.getX instead
18797          * @static
18798          */
18799         getPosX: function(el) {
18800             return Roo.lib.Dom.getX(el);
18801         },
18802
18803         /**
18804          * Returns the Y position of an html element
18805          * @method getPosY
18806          * @param el the element for which to get the position
18807          * @return {int} the Y coordinate
18808          * @deprecated use Roo.lib.Dom.getY instead
18809          * @static
18810          */
18811         getPosY: function(el) {
18812             return Roo.lib.Dom.getY(el);
18813         },
18814
18815         /**
18816          * Swap two nodes.  In IE, we use the native method, for others we
18817          * emulate the IE behavior
18818          * @method swapNode
18819          * @param n1 the first node to swap
18820          * @param n2 the other node to swap
18821          * @static
18822          */
18823         swapNode: function(n1, n2) {
18824             if (n1.swapNode) {
18825                 n1.swapNode(n2);
18826             } else {
18827                 var p = n2.parentNode;
18828                 var s = n2.nextSibling;
18829
18830                 if (s == n1) {
18831                     p.insertBefore(n1, n2);
18832                 } else if (n2 == n1.nextSibling) {
18833                     p.insertBefore(n2, n1);
18834                 } else {
18835                     n1.parentNode.replaceChild(n2, n1);
18836                     p.insertBefore(n1, s);
18837                 }
18838             }
18839         },
18840
18841         /**
18842          * Returns the current scroll position
18843          * @method getScroll
18844          * @private
18845          * @static
18846          */
18847         getScroll: function () {
18848             var t, l, dde=document.documentElement, db=document.body;
18849             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18850                 t = dde.scrollTop;
18851                 l = dde.scrollLeft;
18852             } else if (db) {
18853                 t = db.scrollTop;
18854                 l = db.scrollLeft;
18855             } else {
18856
18857             }
18858             return { top: t, left: l };
18859         },
18860
18861         /**
18862          * Returns the specified element style property
18863          * @method getStyle
18864          * @param {HTMLElement} el          the element
18865          * @param {string}      styleProp   the style property
18866          * @return {string} The value of the style property
18867          * @deprecated use Roo.lib.Dom.getStyle
18868          * @static
18869          */
18870         getStyle: function(el, styleProp) {
18871             return Roo.fly(el).getStyle(styleProp);
18872         },
18873
18874         /**
18875          * Gets the scrollTop
18876          * @method getScrollTop
18877          * @return {int} the document's scrollTop
18878          * @static
18879          */
18880         getScrollTop: function () { return this.getScroll().top; },
18881
18882         /**
18883          * Gets the scrollLeft
18884          * @method getScrollLeft
18885          * @return {int} the document's scrollTop
18886          * @static
18887          */
18888         getScrollLeft: function () { return this.getScroll().left; },
18889
18890         /**
18891          * Sets the x/y position of an element to the location of the
18892          * target element.
18893          * @method moveToEl
18894          * @param {HTMLElement} moveEl      The element to move
18895          * @param {HTMLElement} targetEl    The position reference element
18896          * @static
18897          */
18898         moveToEl: function (moveEl, targetEl) {
18899             var aCoord = Roo.lib.Dom.getXY(targetEl);
18900             Roo.lib.Dom.setXY(moveEl, aCoord);
18901         },
18902
18903         /**
18904          * Numeric array sort function
18905          * @method numericSort
18906          * @static
18907          */
18908         numericSort: function(a, b) { return (a - b); },
18909
18910         /**
18911          * Internal counter
18912          * @property _timeoutCount
18913          * @private
18914          * @static
18915          */
18916         _timeoutCount: 0,
18917
18918         /**
18919          * Trying to make the load order less important.  Without this we get
18920          * an error if this file is loaded before the Event Utility.
18921          * @method _addListeners
18922          * @private
18923          * @static
18924          */
18925         _addListeners: function() {
18926             var DDM = Roo.dd.DDM;
18927             if ( Roo.lib.Event && document ) {
18928                 DDM._onLoad();
18929             } else {
18930                 if (DDM._timeoutCount > 2000) {
18931                 } else {
18932                     setTimeout(DDM._addListeners, 10);
18933                     if (document && document.body) {
18934                         DDM._timeoutCount += 1;
18935                     }
18936                 }
18937             }
18938         },
18939
18940         /**
18941          * Recursively searches the immediate parent and all child nodes for
18942          * the handle element in order to determine wheter or not it was
18943          * clicked.
18944          * @method handleWasClicked
18945          * @param node the html element to inspect
18946          * @static
18947          */
18948         handleWasClicked: function(node, id) {
18949             if (this.isHandle(id, node.id)) {
18950                 return true;
18951             } else {
18952                 // check to see if this is a text node child of the one we want
18953                 var p = node.parentNode;
18954
18955                 while (p) {
18956                     if (this.isHandle(id, p.id)) {
18957                         return true;
18958                     } else {
18959                         p = p.parentNode;
18960                     }
18961                 }
18962             }
18963
18964             return false;
18965         }
18966
18967     };
18968
18969 }();
18970
18971 // shorter alias, save a few bytes
18972 Roo.dd.DDM = Roo.dd.DragDropMgr;
18973 Roo.dd.DDM._addListeners();
18974
18975 }/*
18976  * Based on:
18977  * Ext JS Library 1.1.1
18978  * Copyright(c) 2006-2007, Ext JS, LLC.
18979  *
18980  * Originally Released Under LGPL - original licence link has changed is not relivant.
18981  *
18982  * Fork - LGPL
18983  * <script type="text/javascript">
18984  */
18985
18986 /**
18987  * @class Roo.dd.DD
18988  * A DragDrop implementation where the linked element follows the
18989  * mouse cursor during a drag.
18990  * @extends Roo.dd.DragDrop
18991  * @constructor
18992  * @param {String} id the id of the linked element
18993  * @param {String} sGroup the group of related DragDrop items
18994  * @param {object} config an object containing configurable attributes
18995  *                Valid properties for DD:
18996  *                    scroll
18997  */
18998 Roo.dd.DD = function(id, sGroup, config) {
18999     if (id) {
19000         this.init(id, sGroup, config);
19001     }
19002 };
19003
19004 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
19005
19006     /**
19007      * When set to true, the utility automatically tries to scroll the browser
19008      * window wehn a drag and drop element is dragged near the viewport boundary.
19009      * Defaults to true.
19010      * @property scroll
19011      * @type boolean
19012      */
19013     scroll: true,
19014
19015     /**
19016      * Sets the pointer offset to the distance between the linked element's top
19017      * left corner and the location the element was clicked
19018      * @method autoOffset
19019      * @param {int} iPageX the X coordinate of the click
19020      * @param {int} iPageY the Y coordinate of the click
19021      */
19022     autoOffset: function(iPageX, iPageY) {
19023         var x = iPageX - this.startPageX;
19024         var y = iPageY - this.startPageY;
19025         this.setDelta(x, y);
19026     },
19027
19028     /**
19029      * Sets the pointer offset.  You can call this directly to force the
19030      * offset to be in a particular location (e.g., pass in 0,0 to set it
19031      * to the center of the object)
19032      * @method setDelta
19033      * @param {int} iDeltaX the distance from the left
19034      * @param {int} iDeltaY the distance from the top
19035      */
19036     setDelta: function(iDeltaX, iDeltaY) {
19037         this.deltaX = iDeltaX;
19038         this.deltaY = iDeltaY;
19039     },
19040
19041     /**
19042      * Sets the drag element to the location of the mousedown or click event,
19043      * maintaining the cursor location relative to the location on the element
19044      * that was clicked.  Override this if you want to place the element in a
19045      * location other than where the cursor is.
19046      * @method setDragElPos
19047      * @param {int} iPageX the X coordinate of the mousedown or drag event
19048      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19049      */
19050     setDragElPos: function(iPageX, iPageY) {
19051         // the first time we do this, we are going to check to make sure
19052         // the element has css positioning
19053
19054         var el = this.getDragEl();
19055         this.alignElWithMouse(el, iPageX, iPageY);
19056     },
19057
19058     /**
19059      * Sets the element to the location of the mousedown or click event,
19060      * maintaining the cursor location relative to the location on the element
19061      * that was clicked.  Override this if you want to place the element in a
19062      * location other than where the cursor is.
19063      * @method alignElWithMouse
19064      * @param {HTMLElement} el the element to move
19065      * @param {int} iPageX the X coordinate of the mousedown or drag event
19066      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19067      */
19068     alignElWithMouse: function(el, iPageX, iPageY) {
19069         var oCoord = this.getTargetCoord(iPageX, iPageY);
19070         var fly = el.dom ? el : Roo.fly(el);
19071         if (!this.deltaSetXY) {
19072             var aCoord = [oCoord.x, oCoord.y];
19073             fly.setXY(aCoord);
19074             var newLeft = fly.getLeft(true);
19075             var newTop  = fly.getTop(true);
19076             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19077         } else {
19078             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19079         }
19080
19081         this.cachePosition(oCoord.x, oCoord.y);
19082         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19083         return oCoord;
19084     },
19085
19086     /**
19087      * Saves the most recent position so that we can reset the constraints and
19088      * tick marks on-demand.  We need to know this so that we can calculate the
19089      * number of pixels the element is offset from its original position.
19090      * @method cachePosition
19091      * @param iPageX the current x position (optional, this just makes it so we
19092      * don't have to look it up again)
19093      * @param iPageY the current y position (optional, this just makes it so we
19094      * don't have to look it up again)
19095      */
19096     cachePosition: function(iPageX, iPageY) {
19097         if (iPageX) {
19098             this.lastPageX = iPageX;
19099             this.lastPageY = iPageY;
19100         } else {
19101             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19102             this.lastPageX = aCoord[0];
19103             this.lastPageY = aCoord[1];
19104         }
19105     },
19106
19107     /**
19108      * Auto-scroll the window if the dragged object has been moved beyond the
19109      * visible window boundary.
19110      * @method autoScroll
19111      * @param {int} x the drag element's x position
19112      * @param {int} y the drag element's y position
19113      * @param {int} h the height of the drag element
19114      * @param {int} w the width of the drag element
19115      * @private
19116      */
19117     autoScroll: function(x, y, h, w) {
19118
19119         if (this.scroll) {
19120             // The client height
19121             var clientH = Roo.lib.Dom.getViewWidth();
19122
19123             // The client width
19124             var clientW = Roo.lib.Dom.getViewHeight();
19125
19126             // The amt scrolled down
19127             var st = this.DDM.getScrollTop();
19128
19129             // The amt scrolled right
19130             var sl = this.DDM.getScrollLeft();
19131
19132             // Location of the bottom of the element
19133             var bot = h + y;
19134
19135             // Location of the right of the element
19136             var right = w + x;
19137
19138             // The distance from the cursor to the bottom of the visible area,
19139             // adjusted so that we don't scroll if the cursor is beyond the
19140             // element drag constraints
19141             var toBot = (clientH + st - y - this.deltaY);
19142
19143             // The distance from the cursor to the right of the visible area
19144             var toRight = (clientW + sl - x - this.deltaX);
19145
19146
19147             // How close to the edge the cursor must be before we scroll
19148             // var thresh = (document.all) ? 100 : 40;
19149             var thresh = 40;
19150
19151             // How many pixels to scroll per autoscroll op.  This helps to reduce
19152             // clunky scrolling. IE is more sensitive about this ... it needs this
19153             // value to be higher.
19154             var scrAmt = (document.all) ? 80 : 30;
19155
19156             // Scroll down if we are near the bottom of the visible page and the
19157             // obj extends below the crease
19158             if ( bot > clientH && toBot < thresh ) {
19159                 window.scrollTo(sl, st + scrAmt);
19160             }
19161
19162             // Scroll up if the window is scrolled down and the top of the object
19163             // goes above the top border
19164             if ( y < st && st > 0 && y - st < thresh ) {
19165                 window.scrollTo(sl, st - scrAmt);
19166             }
19167
19168             // Scroll right if the obj is beyond the right border and the cursor is
19169             // near the border.
19170             if ( right > clientW && toRight < thresh ) {
19171                 window.scrollTo(sl + scrAmt, st);
19172             }
19173
19174             // Scroll left if the window has been scrolled to the right and the obj
19175             // extends past the left border
19176             if ( x < sl && sl > 0 && x - sl < thresh ) {
19177                 window.scrollTo(sl - scrAmt, st);
19178             }
19179         }
19180     },
19181
19182     /**
19183      * Finds the location the element should be placed if we want to move
19184      * it to where the mouse location less the click offset would place us.
19185      * @method getTargetCoord
19186      * @param {int} iPageX the X coordinate of the click
19187      * @param {int} iPageY the Y coordinate of the click
19188      * @return an object that contains the coordinates (Object.x and Object.y)
19189      * @private
19190      */
19191     getTargetCoord: function(iPageX, iPageY) {
19192
19193
19194         var x = iPageX - this.deltaX;
19195         var y = iPageY - this.deltaY;
19196
19197         if (this.constrainX) {
19198             if (x < this.minX) { x = this.minX; }
19199             if (x > this.maxX) { x = this.maxX; }
19200         }
19201
19202         if (this.constrainY) {
19203             if (y < this.minY) { y = this.minY; }
19204             if (y > this.maxY) { y = this.maxY; }
19205         }
19206
19207         x = this.getTick(x, this.xTicks);
19208         y = this.getTick(y, this.yTicks);
19209
19210
19211         return {x:x, y:y};
19212     },
19213
19214     /*
19215      * Sets up config options specific to this class. Overrides
19216      * Roo.dd.DragDrop, but all versions of this method through the
19217      * inheritance chain are called
19218      */
19219     applyConfig: function() {
19220         Roo.dd.DD.superclass.applyConfig.call(this);
19221         this.scroll = (this.config.scroll !== false);
19222     },
19223
19224     /*
19225      * Event that fires prior to the onMouseDown event.  Overrides
19226      * Roo.dd.DragDrop.
19227      */
19228     b4MouseDown: function(e) {
19229         // this.resetConstraints();
19230         this.autoOffset(e.getPageX(),
19231                             e.getPageY());
19232     },
19233
19234     /*
19235      * Event that fires prior to the onDrag event.  Overrides
19236      * Roo.dd.DragDrop.
19237      */
19238     b4Drag: function(e) {
19239         this.setDragElPos(e.getPageX(),
19240                             e.getPageY());
19241     },
19242
19243     toString: function() {
19244         return ("DD " + this.id);
19245     }
19246
19247     //////////////////////////////////////////////////////////////////////////
19248     // Debugging ygDragDrop events that can be overridden
19249     //////////////////////////////////////////////////////////////////////////
19250     /*
19251     startDrag: function(x, y) {
19252     },
19253
19254     onDrag: function(e) {
19255     },
19256
19257     onDragEnter: function(e, id) {
19258     },
19259
19260     onDragOver: function(e, id) {
19261     },
19262
19263     onDragOut: function(e, id) {
19264     },
19265
19266     onDragDrop: function(e, id) {
19267     },
19268
19269     endDrag: function(e) {
19270     }
19271
19272     */
19273
19274 });/*
19275  * Based on:
19276  * Ext JS Library 1.1.1
19277  * Copyright(c) 2006-2007, Ext JS, LLC.
19278  *
19279  * Originally Released Under LGPL - original licence link has changed is not relivant.
19280  *
19281  * Fork - LGPL
19282  * <script type="text/javascript">
19283  */
19284
19285 /**
19286  * @class Roo.dd.DDProxy
19287  * A DragDrop implementation that inserts an empty, bordered div into
19288  * the document that follows the cursor during drag operations.  At the time of
19289  * the click, the frame div is resized to the dimensions of the linked html
19290  * element, and moved to the exact location of the linked element.
19291  *
19292  * References to the "frame" element refer to the single proxy element that
19293  * was created to be dragged in place of all DDProxy elements on the
19294  * page.
19295  *
19296  * @extends Roo.dd.DD
19297  * @constructor
19298  * @param {String} id the id of the linked html element
19299  * @param {String} sGroup the group of related DragDrop objects
19300  * @param {object} config an object containing configurable attributes
19301  *                Valid properties for DDProxy in addition to those in DragDrop:
19302  *                   resizeFrame, centerFrame, dragElId
19303  */
19304 Roo.dd.DDProxy = function(id, sGroup, config) {
19305     if (id) {
19306         this.init(id, sGroup, config);
19307         this.initFrame();
19308     }
19309 };
19310
19311 /**
19312  * The default drag frame div id
19313  * @property Roo.dd.DDProxy.dragElId
19314  * @type String
19315  * @static
19316  */
19317 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19318
19319 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19320
19321     /**
19322      * By default we resize the drag frame to be the same size as the element
19323      * we want to drag (this is to get the frame effect).  We can turn it off
19324      * if we want a different behavior.
19325      * @property resizeFrame
19326      * @type boolean
19327      */
19328     resizeFrame: true,
19329
19330     /**
19331      * By default the frame is positioned exactly where the drag element is, so
19332      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19333      * you do not have constraints on the obj is to have the drag frame centered
19334      * around the cursor.  Set centerFrame to true for this effect.
19335      * @property centerFrame
19336      * @type boolean
19337      */
19338     centerFrame: false,
19339
19340     /**
19341      * Creates the proxy element if it does not yet exist
19342      * @method createFrame
19343      */
19344     createFrame: function() {
19345         var self = this;
19346         var body = document.body;
19347
19348         if (!body || !body.firstChild) {
19349             setTimeout( function() { self.createFrame(); }, 50 );
19350             return;
19351         }
19352
19353         var div = this.getDragEl();
19354
19355         if (!div) {
19356             div    = document.createElement("div");
19357             div.id = this.dragElId;
19358             var s  = div.style;
19359
19360             s.position   = "absolute";
19361             s.visibility = "hidden";
19362             s.cursor     = "move";
19363             s.border     = "2px solid #aaa";
19364             s.zIndex     = 999;
19365
19366             // appendChild can blow up IE if invoked prior to the window load event
19367             // while rendering a table.  It is possible there are other scenarios
19368             // that would cause this to happen as well.
19369             body.insertBefore(div, body.firstChild);
19370         }
19371     },
19372
19373     /**
19374      * Initialization for the drag frame element.  Must be called in the
19375      * constructor of all subclasses
19376      * @method initFrame
19377      */
19378     initFrame: function() {
19379         this.createFrame();
19380     },
19381
19382     applyConfig: function() {
19383         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19384
19385         this.resizeFrame = (this.config.resizeFrame !== false);
19386         this.centerFrame = (this.config.centerFrame);
19387         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19388     },
19389
19390     /**
19391      * Resizes the drag frame to the dimensions of the clicked object, positions
19392      * it over the object, and finally displays it
19393      * @method showFrame
19394      * @param {int} iPageX X click position
19395      * @param {int} iPageY Y click position
19396      * @private
19397      */
19398     showFrame: function(iPageX, iPageY) {
19399         var el = this.getEl();
19400         var dragEl = this.getDragEl();
19401         var s = dragEl.style;
19402
19403         this._resizeProxy();
19404
19405         if (this.centerFrame) {
19406             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19407                            Math.round(parseInt(s.height, 10)/2) );
19408         }
19409
19410         this.setDragElPos(iPageX, iPageY);
19411
19412         Roo.fly(dragEl).show();
19413     },
19414
19415     /**
19416      * The proxy is automatically resized to the dimensions of the linked
19417      * element when a drag is initiated, unless resizeFrame is set to false
19418      * @method _resizeProxy
19419      * @private
19420      */
19421     _resizeProxy: function() {
19422         if (this.resizeFrame) {
19423             var el = this.getEl();
19424             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19425         }
19426     },
19427
19428     // overrides Roo.dd.DragDrop
19429     b4MouseDown: function(e) {
19430         var x = e.getPageX();
19431         var y = e.getPageY();
19432         this.autoOffset(x, y);
19433         this.setDragElPos(x, y);
19434     },
19435
19436     // overrides Roo.dd.DragDrop
19437     b4StartDrag: function(x, y) {
19438         // show the drag frame
19439         this.showFrame(x, y);
19440     },
19441
19442     // overrides Roo.dd.DragDrop
19443     b4EndDrag: function(e) {
19444         Roo.fly(this.getDragEl()).hide();
19445     },
19446
19447     // overrides Roo.dd.DragDrop
19448     // By default we try to move the element to the last location of the frame.
19449     // This is so that the default behavior mirrors that of Roo.dd.DD.
19450     endDrag: function(e) {
19451
19452         var lel = this.getEl();
19453         var del = this.getDragEl();
19454
19455         // Show the drag frame briefly so we can get its position
19456         del.style.visibility = "";
19457
19458         this.beforeMove();
19459         // Hide the linked element before the move to get around a Safari
19460         // rendering bug.
19461         lel.style.visibility = "hidden";
19462         Roo.dd.DDM.moveToEl(lel, del);
19463         del.style.visibility = "hidden";
19464         lel.style.visibility = "";
19465
19466         this.afterDrag();
19467     },
19468
19469     beforeMove : function(){
19470
19471     },
19472
19473     afterDrag : function(){
19474
19475     },
19476
19477     toString: function() {
19478         return ("DDProxy " + this.id);
19479     }
19480
19481 });
19482 /*
19483  * Based on:
19484  * Ext JS Library 1.1.1
19485  * Copyright(c) 2006-2007, Ext JS, LLC.
19486  *
19487  * Originally Released Under LGPL - original licence link has changed is not relivant.
19488  *
19489  * Fork - LGPL
19490  * <script type="text/javascript">
19491  */
19492
19493  /**
19494  * @class Roo.dd.DDTarget
19495  * A DragDrop implementation that does not move, but can be a drop
19496  * target.  You would get the same result by simply omitting implementation
19497  * for the event callbacks, but this way we reduce the processing cost of the
19498  * event listener and the callbacks.
19499  * @extends Roo.dd.DragDrop
19500  * @constructor
19501  * @param {String} id the id of the element that is a drop target
19502  * @param {String} sGroup the group of related DragDrop objects
19503  * @param {object} config an object containing configurable attributes
19504  *                 Valid properties for DDTarget in addition to those in
19505  *                 DragDrop:
19506  *                    none
19507  */
19508 Roo.dd.DDTarget = function(id, sGroup, config) {
19509     if (id) {
19510         this.initTarget(id, sGroup, config);
19511     }
19512     if (config.listeners || config.events) { 
19513        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19514             listeners : config.listeners || {}, 
19515             events : config.events || {} 
19516         });    
19517     }
19518 };
19519
19520 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19521 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19522     toString: function() {
19523         return ("DDTarget " + this.id);
19524     }
19525 });
19526 /*
19527  * Based on:
19528  * Ext JS Library 1.1.1
19529  * Copyright(c) 2006-2007, Ext JS, LLC.
19530  *
19531  * Originally Released Under LGPL - original licence link has changed is not relivant.
19532  *
19533  * Fork - LGPL
19534  * <script type="text/javascript">
19535  */
19536  
19537
19538 /**
19539  * @class Roo.dd.ScrollManager
19540  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19541  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19542  * @singleton
19543  */
19544 Roo.dd.ScrollManager = function(){
19545     var ddm = Roo.dd.DragDropMgr;
19546     var els = {};
19547     var dragEl = null;
19548     var proc = {};
19549     
19550     
19551     
19552     var onStop = function(e){
19553         dragEl = null;
19554         clearProc();
19555     };
19556     
19557     var triggerRefresh = function(){
19558         if(ddm.dragCurrent){
19559              ddm.refreshCache(ddm.dragCurrent.groups);
19560         }
19561     };
19562     
19563     var doScroll = function(){
19564         if(ddm.dragCurrent){
19565             var dds = Roo.dd.ScrollManager;
19566             if(!dds.animate){
19567                 if(proc.el.scroll(proc.dir, dds.increment)){
19568                     triggerRefresh();
19569                 }
19570             }else{
19571                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19572             }
19573         }
19574     };
19575     
19576     var clearProc = function(){
19577         if(proc.id){
19578             clearInterval(proc.id);
19579         }
19580         proc.id = 0;
19581         proc.el = null;
19582         proc.dir = "";
19583     };
19584     
19585     var startProc = function(el, dir){
19586          Roo.log('scroll startproc');
19587         clearProc();
19588         proc.el = el;
19589         proc.dir = dir;
19590         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19591     };
19592     
19593     var onFire = function(e, isDrop){
19594        
19595         if(isDrop || !ddm.dragCurrent){ return; }
19596         var dds = Roo.dd.ScrollManager;
19597         if(!dragEl || dragEl != ddm.dragCurrent){
19598             dragEl = ddm.dragCurrent;
19599             // refresh regions on drag start
19600             dds.refreshCache();
19601         }
19602         
19603         var xy = Roo.lib.Event.getXY(e);
19604         var pt = new Roo.lib.Point(xy[0], xy[1]);
19605         for(var id in els){
19606             var el = els[id], r = el._region;
19607             if(r && r.contains(pt) && el.isScrollable()){
19608                 if(r.bottom - pt.y <= dds.thresh){
19609                     if(proc.el != el){
19610                         startProc(el, "down");
19611                     }
19612                     return;
19613                 }else if(r.right - pt.x <= dds.thresh){
19614                     if(proc.el != el){
19615                         startProc(el, "left");
19616                     }
19617                     return;
19618                 }else if(pt.y - r.top <= dds.thresh){
19619                     if(proc.el != el){
19620                         startProc(el, "up");
19621                     }
19622                     return;
19623                 }else if(pt.x - r.left <= dds.thresh){
19624                     if(proc.el != el){
19625                         startProc(el, "right");
19626                     }
19627                     return;
19628                 }
19629             }
19630         }
19631         clearProc();
19632     };
19633     
19634     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19635     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19636     
19637     return {
19638         /**
19639          * Registers new overflow element(s) to auto scroll
19640          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19641          */
19642         register : function(el){
19643             if(el instanceof Array){
19644                 for(var i = 0, len = el.length; i < len; i++) {
19645                         this.register(el[i]);
19646                 }
19647             }else{
19648                 el = Roo.get(el);
19649                 els[el.id] = el;
19650             }
19651             Roo.dd.ScrollManager.els = els;
19652         },
19653         
19654         /**
19655          * Unregisters overflow element(s) so they are no longer scrolled
19656          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19657          */
19658         unregister : function(el){
19659             if(el instanceof Array){
19660                 for(var i = 0, len = el.length; i < len; i++) {
19661                         this.unregister(el[i]);
19662                 }
19663             }else{
19664                 el = Roo.get(el);
19665                 delete els[el.id];
19666             }
19667         },
19668         
19669         /**
19670          * The number of pixels from the edge of a container the pointer needs to be to 
19671          * trigger scrolling (defaults to 25)
19672          * @type Number
19673          */
19674         thresh : 25,
19675         
19676         /**
19677          * The number of pixels to scroll in each scroll increment (defaults to 50)
19678          * @type Number
19679          */
19680         increment : 100,
19681         
19682         /**
19683          * The frequency of scrolls in milliseconds (defaults to 500)
19684          * @type Number
19685          */
19686         frequency : 500,
19687         
19688         /**
19689          * True to animate the scroll (defaults to true)
19690          * @type Boolean
19691          */
19692         animate: true,
19693         
19694         /**
19695          * The animation duration in seconds - 
19696          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19697          * @type Number
19698          */
19699         animDuration: .4,
19700         
19701         /**
19702          * Manually trigger a cache refresh.
19703          */
19704         refreshCache : function(){
19705             for(var id in els){
19706                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19707                     els[id]._region = els[id].getRegion();
19708                 }
19709             }
19710         }
19711     };
19712 }();/*
19713  * Based on:
19714  * Ext JS Library 1.1.1
19715  * Copyright(c) 2006-2007, Ext JS, LLC.
19716  *
19717  * Originally Released Under LGPL - original licence link has changed is not relivant.
19718  *
19719  * Fork - LGPL
19720  * <script type="text/javascript">
19721  */
19722  
19723
19724 /**
19725  * @class Roo.dd.Registry
19726  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19727  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19728  * @singleton
19729  */
19730 Roo.dd.Registry = function(){
19731     var elements = {}; 
19732     var handles = {}; 
19733     var autoIdSeed = 0;
19734
19735     var getId = function(el, autogen){
19736         if(typeof el == "string"){
19737             return el;
19738         }
19739         var id = el.id;
19740         if(!id && autogen !== false){
19741             id = "roodd-" + (++autoIdSeed);
19742             el.id = id;
19743         }
19744         return id;
19745     };
19746     
19747     return {
19748     /**
19749      * Register a drag drop element
19750      * @param {String|HTMLElement} element The id or DOM node to register
19751      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19752      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19753      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19754      * populated in the data object (if applicable):
19755      * <pre>
19756 Value      Description<br />
19757 ---------  ------------------------------------------<br />
19758 handles    Array of DOM nodes that trigger dragging<br />
19759            for the element being registered<br />
19760 isHandle   True if the element passed in triggers<br />
19761            dragging itself, else false
19762 </pre>
19763      */
19764         register : function(el, data){
19765             data = data || {};
19766             if(typeof el == "string"){
19767                 el = document.getElementById(el);
19768             }
19769             data.ddel = el;
19770             elements[getId(el)] = data;
19771             if(data.isHandle !== false){
19772                 handles[data.ddel.id] = data;
19773             }
19774             if(data.handles){
19775                 var hs = data.handles;
19776                 for(var i = 0, len = hs.length; i < len; i++){
19777                         handles[getId(hs[i])] = data;
19778                 }
19779             }
19780         },
19781
19782     /**
19783      * Unregister a drag drop element
19784      * @param {String|HTMLElement}  element The id or DOM node to unregister
19785      */
19786         unregister : function(el){
19787             var id = getId(el, false);
19788             var data = elements[id];
19789             if(data){
19790                 delete elements[id];
19791                 if(data.handles){
19792                     var hs = data.handles;
19793                     for(var i = 0, len = hs.length; i < len; i++){
19794                         delete handles[getId(hs[i], false)];
19795                     }
19796                 }
19797             }
19798         },
19799
19800     /**
19801      * Returns the handle registered for a DOM Node by id
19802      * @param {String|HTMLElement} id The DOM node or id to look up
19803      * @return {Object} handle The custom handle data
19804      */
19805         getHandle : function(id){
19806             if(typeof id != "string"){ // must be element?
19807                 id = id.id;
19808             }
19809             return handles[id];
19810         },
19811
19812     /**
19813      * Returns the handle that is registered for the DOM node that is the target of the event
19814      * @param {Event} e The event
19815      * @return {Object} handle The custom handle data
19816      */
19817         getHandleFromEvent : function(e){
19818             var t = Roo.lib.Event.getTarget(e);
19819             return t ? handles[t.id] : null;
19820         },
19821
19822     /**
19823      * Returns a custom data object that is registered for a DOM node by id
19824      * @param {String|HTMLElement} id The DOM node or id to look up
19825      * @return {Object} data The custom data
19826      */
19827         getTarget : function(id){
19828             if(typeof id != "string"){ // must be element?
19829                 id = id.id;
19830             }
19831             return elements[id];
19832         },
19833
19834     /**
19835      * Returns a custom data object that is registered for the DOM node that is the target of the event
19836      * @param {Event} e The event
19837      * @return {Object} data The custom data
19838      */
19839         getTargetFromEvent : function(e){
19840             var t = Roo.lib.Event.getTarget(e);
19841             return t ? elements[t.id] || handles[t.id] : null;
19842         }
19843     };
19844 }();/*
19845  * Based on:
19846  * Ext JS Library 1.1.1
19847  * Copyright(c) 2006-2007, Ext JS, LLC.
19848  *
19849  * Originally Released Under LGPL - original licence link has changed is not relivant.
19850  *
19851  * Fork - LGPL
19852  * <script type="text/javascript">
19853  */
19854  
19855
19856 /**
19857  * @class Roo.dd.StatusProxy
19858  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19859  * default drag proxy used by all Roo.dd components.
19860  * @constructor
19861  * @param {Object} config
19862  */
19863 Roo.dd.StatusProxy = function(config){
19864     Roo.apply(this, config);
19865     this.id = this.id || Roo.id();
19866     this.el = new Roo.Layer({
19867         dh: {
19868             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19869                 {tag: "div", cls: "x-dd-drop-icon"},
19870                 {tag: "div", cls: "x-dd-drag-ghost"}
19871             ]
19872         }, 
19873         shadow: !config || config.shadow !== false
19874     });
19875     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19876     this.dropStatus = this.dropNotAllowed;
19877 };
19878
19879 Roo.dd.StatusProxy.prototype = {
19880     /**
19881      * @cfg {String} dropAllowed
19882      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19883      */
19884     dropAllowed : "x-dd-drop-ok",
19885     /**
19886      * @cfg {String} dropNotAllowed
19887      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19888      */
19889     dropNotAllowed : "x-dd-drop-nodrop",
19890
19891     /**
19892      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19893      * over the current target element.
19894      * @param {String} cssClass The css class for the new drop status indicator image
19895      */
19896     setStatus : function(cssClass){
19897         cssClass = cssClass || this.dropNotAllowed;
19898         if(this.dropStatus != cssClass){
19899             this.el.replaceClass(this.dropStatus, cssClass);
19900             this.dropStatus = cssClass;
19901         }
19902     },
19903
19904     /**
19905      * Resets the status indicator to the default dropNotAllowed value
19906      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19907      */
19908     reset : function(clearGhost){
19909         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19910         this.dropStatus = this.dropNotAllowed;
19911         if(clearGhost){
19912             this.ghost.update("");
19913         }
19914     },
19915
19916     /**
19917      * Updates the contents of the ghost element
19918      * @param {String} html The html that will replace the current innerHTML of the ghost element
19919      */
19920     update : function(html){
19921         if(typeof html == "string"){
19922             this.ghost.update(html);
19923         }else{
19924             this.ghost.update("");
19925             html.style.margin = "0";
19926             this.ghost.dom.appendChild(html);
19927         }
19928         // ensure float = none set?? cant remember why though.
19929         var el = this.ghost.dom.firstChild;
19930                 if(el){
19931                         Roo.fly(el).setStyle('float', 'none');
19932                 }
19933     },
19934     
19935     /**
19936      * Returns the underlying proxy {@link Roo.Layer}
19937      * @return {Roo.Layer} el
19938     */
19939     getEl : function(){
19940         return this.el;
19941     },
19942
19943     /**
19944      * Returns the ghost element
19945      * @return {Roo.Element} el
19946      */
19947     getGhost : function(){
19948         return this.ghost;
19949     },
19950
19951     /**
19952      * Hides the proxy
19953      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19954      */
19955     hide : function(clear){
19956         this.el.hide();
19957         if(clear){
19958             this.reset(true);
19959         }
19960     },
19961
19962     /**
19963      * Stops the repair animation if it's currently running
19964      */
19965     stop : function(){
19966         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19967             this.anim.stop();
19968         }
19969     },
19970
19971     /**
19972      * Displays this proxy
19973      */
19974     show : function(){
19975         this.el.show();
19976     },
19977
19978     /**
19979      * Force the Layer to sync its shadow and shim positions to the element
19980      */
19981     sync : function(){
19982         this.el.sync();
19983     },
19984
19985     /**
19986      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19987      * invalid drop operation by the item being dragged.
19988      * @param {Array} xy The XY position of the element ([x, y])
19989      * @param {Function} callback The function to call after the repair is complete
19990      * @param {Object} scope The scope in which to execute the callback
19991      */
19992     repair : function(xy, callback, scope){
19993         this.callback = callback;
19994         this.scope = scope;
19995         if(xy && this.animRepair !== false){
19996             this.el.addClass("x-dd-drag-repair");
19997             this.el.hideUnders(true);
19998             this.anim = this.el.shift({
19999                 duration: this.repairDuration || .5,
20000                 easing: 'easeOut',
20001                 xy: xy,
20002                 stopFx: true,
20003                 callback: this.afterRepair,
20004                 scope: this
20005             });
20006         }else{
20007             this.afterRepair();
20008         }
20009     },
20010
20011     // private
20012     afterRepair : function(){
20013         this.hide(true);
20014         if(typeof this.callback == "function"){
20015             this.callback.call(this.scope || this);
20016         }
20017         this.callback = null;
20018         this.scope = null;
20019     }
20020 };/*
20021  * Based on:
20022  * Ext JS Library 1.1.1
20023  * Copyright(c) 2006-2007, Ext JS, LLC.
20024  *
20025  * Originally Released Under LGPL - original licence link has changed is not relivant.
20026  *
20027  * Fork - LGPL
20028  * <script type="text/javascript">
20029  */
20030
20031 /**
20032  * @class Roo.dd.DragSource
20033  * @extends Roo.dd.DDProxy
20034  * A simple class that provides the basic implementation needed to make any element draggable.
20035  * @constructor
20036  * @param {String/HTMLElement/Element} el The container element
20037  * @param {Object} config
20038  */
20039 Roo.dd.DragSource = function(el, config){
20040     this.el = Roo.get(el);
20041     this.dragData = {};
20042     
20043     Roo.apply(this, config);
20044     
20045     if(!this.proxy){
20046         this.proxy = new Roo.dd.StatusProxy();
20047     }
20048
20049     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20050           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20051     
20052     this.dragging = false;
20053 };
20054
20055 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20056     /**
20057      * @cfg {String} dropAllowed
20058      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20059      */
20060     dropAllowed : "x-dd-drop-ok",
20061     /**
20062      * @cfg {String} dropNotAllowed
20063      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20064      */
20065     dropNotAllowed : "x-dd-drop-nodrop",
20066
20067     /**
20068      * Returns the data object associated with this drag source
20069      * @return {Object} data An object containing arbitrary data
20070      */
20071     getDragData : function(e){
20072         return this.dragData;
20073     },
20074
20075     // private
20076     onDragEnter : function(e, id){
20077         var target = Roo.dd.DragDropMgr.getDDById(id);
20078         this.cachedTarget = target;
20079         if(this.beforeDragEnter(target, e, id) !== false){
20080             if(target.isNotifyTarget){
20081                 var status = target.notifyEnter(this, e, this.dragData);
20082                 this.proxy.setStatus(status);
20083             }else{
20084                 this.proxy.setStatus(this.dropAllowed);
20085             }
20086             
20087             if(this.afterDragEnter){
20088                 /**
20089                  * An empty function by default, but provided so that you can perform a custom action
20090                  * when the dragged item enters the drop target by providing an implementation.
20091                  * @param {Roo.dd.DragDrop} target The drop target
20092                  * @param {Event} e The event object
20093                  * @param {String} id The id of the dragged element
20094                  * @method afterDragEnter
20095                  */
20096                 this.afterDragEnter(target, e, id);
20097             }
20098         }
20099     },
20100
20101     /**
20102      * An empty function by default, but provided so that you can perform a custom action
20103      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20104      * @param {Roo.dd.DragDrop} target The drop target
20105      * @param {Event} e The event object
20106      * @param {String} id The id of the dragged element
20107      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20108      */
20109     beforeDragEnter : function(target, e, id){
20110         return true;
20111     },
20112
20113     // private
20114     alignElWithMouse: function() {
20115         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20116         this.proxy.sync();
20117     },
20118
20119     // private
20120     onDragOver : function(e, id){
20121         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20122         if(this.beforeDragOver(target, e, id) !== false){
20123             if(target.isNotifyTarget){
20124                 var status = target.notifyOver(this, e, this.dragData);
20125                 this.proxy.setStatus(status);
20126             }
20127
20128             if(this.afterDragOver){
20129                 /**
20130                  * An empty function by default, but provided so that you can perform a custom action
20131                  * while the dragged item is over the drop target by providing an implementation.
20132                  * @param {Roo.dd.DragDrop} target The drop target
20133                  * @param {Event} e The event object
20134                  * @param {String} id The id of the dragged element
20135                  * @method afterDragOver
20136                  */
20137                 this.afterDragOver(target, e, id);
20138             }
20139         }
20140     },
20141
20142     /**
20143      * An empty function by default, but provided so that you can perform a custom action
20144      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20145      * @param {Roo.dd.DragDrop} target The drop target
20146      * @param {Event} e The event object
20147      * @param {String} id The id of the dragged element
20148      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20149      */
20150     beforeDragOver : function(target, e, id){
20151         return true;
20152     },
20153
20154     // private
20155     onDragOut : function(e, id){
20156         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20157         if(this.beforeDragOut(target, e, id) !== false){
20158             if(target.isNotifyTarget){
20159                 target.notifyOut(this, e, this.dragData);
20160             }
20161             this.proxy.reset();
20162             if(this.afterDragOut){
20163                 /**
20164                  * An empty function by default, but provided so that you can perform a custom action
20165                  * after the dragged item is dragged out of the target without dropping.
20166                  * @param {Roo.dd.DragDrop} target The drop target
20167                  * @param {Event} e The event object
20168                  * @param {String} id The id of the dragged element
20169                  * @method afterDragOut
20170                  */
20171                 this.afterDragOut(target, e, id);
20172             }
20173         }
20174         this.cachedTarget = null;
20175     },
20176
20177     /**
20178      * An empty function by default, but provided so that you can perform a custom action before the dragged
20179      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20180      * @param {Roo.dd.DragDrop} target The drop target
20181      * @param {Event} e The event object
20182      * @param {String} id The id of the dragged element
20183      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20184      */
20185     beforeDragOut : function(target, e, id){
20186         return true;
20187     },
20188     
20189     // private
20190     onDragDrop : function(e, id){
20191         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20192         if(this.beforeDragDrop(target, e, id) !== false){
20193             if(target.isNotifyTarget){
20194                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20195                     this.onValidDrop(target, e, id);
20196                 }else{
20197                     this.onInvalidDrop(target, e, id);
20198                 }
20199             }else{
20200                 this.onValidDrop(target, e, id);
20201             }
20202             
20203             if(this.afterDragDrop){
20204                 /**
20205                  * An empty function by default, but provided so that you can perform a custom action
20206                  * after a valid drag drop has occurred by providing an implementation.
20207                  * @param {Roo.dd.DragDrop} target The drop target
20208                  * @param {Event} e The event object
20209                  * @param {String} id The id of the dropped element
20210                  * @method afterDragDrop
20211                  */
20212                 this.afterDragDrop(target, e, id);
20213             }
20214         }
20215         delete this.cachedTarget;
20216     },
20217
20218     /**
20219      * An empty function by default, but provided so that you can perform a custom action before the dragged
20220      * item is dropped onto the target and optionally cancel the onDragDrop.
20221      * @param {Roo.dd.DragDrop} target The drop target
20222      * @param {Event} e The event object
20223      * @param {String} id The id of the dragged element
20224      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20225      */
20226     beforeDragDrop : function(target, e, id){
20227         return true;
20228     },
20229
20230     // private
20231     onValidDrop : function(target, e, id){
20232         this.hideProxy();
20233         if(this.afterValidDrop){
20234             /**
20235              * An empty function by default, but provided so that you can perform a custom action
20236              * after a valid drop has occurred by providing an implementation.
20237              * @param {Object} target The target DD 
20238              * @param {Event} e The event object
20239              * @param {String} id The id of the dropped element
20240              * @method afterInvalidDrop
20241              */
20242             this.afterValidDrop(target, e, id);
20243         }
20244     },
20245
20246     // private
20247     getRepairXY : function(e, data){
20248         return this.el.getXY();  
20249     },
20250
20251     // private
20252     onInvalidDrop : function(target, e, id){
20253         this.beforeInvalidDrop(target, e, id);
20254         if(this.cachedTarget){
20255             if(this.cachedTarget.isNotifyTarget){
20256                 this.cachedTarget.notifyOut(this, e, this.dragData);
20257             }
20258             this.cacheTarget = null;
20259         }
20260         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20261
20262         if(this.afterInvalidDrop){
20263             /**
20264              * An empty function by default, but provided so that you can perform a custom action
20265              * after an invalid drop has occurred by providing an implementation.
20266              * @param {Event} e The event object
20267              * @param {String} id The id of the dropped element
20268              * @method afterInvalidDrop
20269              */
20270             this.afterInvalidDrop(e, id);
20271         }
20272     },
20273
20274     // private
20275     afterRepair : function(){
20276         if(Roo.enableFx){
20277             this.el.highlight(this.hlColor || "c3daf9");
20278         }
20279         this.dragging = false;
20280     },
20281
20282     /**
20283      * An empty function by default, but provided so that you can perform a custom action after an invalid
20284      * drop has occurred.
20285      * @param {Roo.dd.DragDrop} target The drop target
20286      * @param {Event} e The event object
20287      * @param {String} id The id of the dragged element
20288      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20289      */
20290     beforeInvalidDrop : function(target, e, id){
20291         return true;
20292     },
20293
20294     // private
20295     handleMouseDown : function(e){
20296         if(this.dragging) {
20297             return;
20298         }
20299         var data = this.getDragData(e);
20300         if(data && this.onBeforeDrag(data, e) !== false){
20301             this.dragData = data;
20302             this.proxy.stop();
20303             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20304         } 
20305     },
20306
20307     /**
20308      * An empty function by default, but provided so that you can perform a custom action before the initial
20309      * drag event begins and optionally cancel it.
20310      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20311      * @param {Event} e The event object
20312      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20313      */
20314     onBeforeDrag : function(data, e){
20315         return true;
20316     },
20317
20318     /**
20319      * An empty function by default, but provided so that you can perform a custom action once the initial
20320      * drag event has begun.  The drag cannot be canceled from this function.
20321      * @param {Number} x The x position of the click on the dragged object
20322      * @param {Number} y The y position of the click on the dragged object
20323      */
20324     onStartDrag : Roo.emptyFn,
20325
20326     // private - YUI override
20327     startDrag : function(x, y){
20328         this.proxy.reset();
20329         this.dragging = true;
20330         this.proxy.update("");
20331         this.onInitDrag(x, y);
20332         this.proxy.show();
20333     },
20334
20335     // private
20336     onInitDrag : function(x, y){
20337         var clone = this.el.dom.cloneNode(true);
20338         clone.id = Roo.id(); // prevent duplicate ids
20339         this.proxy.update(clone);
20340         this.onStartDrag(x, y);
20341         return true;
20342     },
20343
20344     /**
20345      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20346      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20347      */
20348     getProxy : function(){
20349         return this.proxy;  
20350     },
20351
20352     /**
20353      * Hides the drag source's {@link Roo.dd.StatusProxy}
20354      */
20355     hideProxy : function(){
20356         this.proxy.hide();  
20357         this.proxy.reset(true);
20358         this.dragging = false;
20359     },
20360
20361     // private
20362     triggerCacheRefresh : function(){
20363         Roo.dd.DDM.refreshCache(this.groups);
20364     },
20365
20366     // private - override to prevent hiding
20367     b4EndDrag: function(e) {
20368     },
20369
20370     // private - override to prevent moving
20371     endDrag : function(e){
20372         this.onEndDrag(this.dragData, e);
20373     },
20374
20375     // private
20376     onEndDrag : function(data, e){
20377     },
20378     
20379     // private - pin to cursor
20380     autoOffset : function(x, y) {
20381         this.setDelta(-12, -20);
20382     }    
20383 });/*
20384  * Based on:
20385  * Ext JS Library 1.1.1
20386  * Copyright(c) 2006-2007, Ext JS, LLC.
20387  *
20388  * Originally Released Under LGPL - original licence link has changed is not relivant.
20389  *
20390  * Fork - LGPL
20391  * <script type="text/javascript">
20392  */
20393
20394
20395 /**
20396  * @class Roo.dd.DropTarget
20397  * @extends Roo.dd.DDTarget
20398  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20399  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20400  * @constructor
20401  * @param {String/HTMLElement/Element} el The container element
20402  * @param {Object} config
20403  */
20404 Roo.dd.DropTarget = function(el, config){
20405     this.el = Roo.get(el);
20406     
20407     var listeners = false; ;
20408     if (config && config.listeners) {
20409         listeners= config.listeners;
20410         delete config.listeners;
20411     }
20412     Roo.apply(this, config);
20413     
20414     if(this.containerScroll){
20415         Roo.dd.ScrollManager.register(this.el);
20416     }
20417     this.addEvents( {
20418          /**
20419          * @scope Roo.dd.DropTarget
20420          */
20421          
20422          /**
20423          * @event enter
20424          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20425          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20426          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20427          * 
20428          * IMPORTANT : it should set this.overClass and this.dropAllowed
20429          * 
20430          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20431          * @param {Event} e The event
20432          * @param {Object} data An object containing arbitrary data supplied by the drag source
20433          */
20434         "enter" : true,
20435         
20436          /**
20437          * @event over
20438          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20439          * This method will be called on every mouse movement while the drag source is over the drop target.
20440          * This default implementation simply returns the dropAllowed config value.
20441          * 
20442          * IMPORTANT : it should set this.dropAllowed
20443          * 
20444          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20445          * @param {Event} e The event
20446          * @param {Object} data An object containing arbitrary data supplied by the drag source
20447          
20448          */
20449         "over" : true,
20450         /**
20451          * @event out
20452          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20453          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20454          * overClass (if any) from the drop element.
20455          * 
20456          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20457          * @param {Event} e The event
20458          * @param {Object} data An object containing arbitrary data supplied by the drag source
20459          */
20460          "out" : true,
20461          
20462         /**
20463          * @event drop
20464          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20465          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20466          * implementation that does something to process the drop event and returns true so that the drag source's
20467          * repair action does not run.
20468          * 
20469          * IMPORTANT : it should set this.success
20470          * 
20471          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20472          * @param {Event} e The event
20473          * @param {Object} data An object containing arbitrary data supplied by the drag source
20474         */
20475          "drop" : true
20476     });
20477             
20478      
20479     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20480         this.el.dom, 
20481         this.ddGroup || this.group,
20482         {
20483             isTarget: true,
20484             listeners : listeners || {} 
20485            
20486         
20487         }
20488     );
20489
20490 };
20491
20492 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20493     /**
20494      * @cfg {String} overClass
20495      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20496      */
20497      /**
20498      * @cfg {String} ddGroup
20499      * The drag drop group to handle drop events for
20500      */
20501      
20502     /**
20503      * @cfg {String} dropAllowed
20504      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20505      */
20506     dropAllowed : "x-dd-drop-ok",
20507     /**
20508      * @cfg {String} dropNotAllowed
20509      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20510      */
20511     dropNotAllowed : "x-dd-drop-nodrop",
20512     /**
20513      * @cfg {boolean} success
20514      * set this after drop listener.. 
20515      */
20516     success : false,
20517     /**
20518      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20519      * if the drop point is valid for over/enter..
20520      */
20521     valid : false,
20522     // private
20523     isTarget : true,
20524
20525     // private
20526     isNotifyTarget : true,
20527     
20528     /**
20529      * @hide
20530      */
20531     notifyEnter : function(dd, e, data)
20532     {
20533         this.valid = true;
20534         this.fireEvent('enter', dd, e, data);
20535         if(this.overClass){
20536             this.el.addClass(this.overClass);
20537         }
20538         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20539             this.valid ? this.dropAllowed : this.dropNotAllowed
20540         );
20541     },
20542
20543     /**
20544      * @hide
20545      */
20546     notifyOver : function(dd, e, data)
20547     {
20548         this.valid = true;
20549         this.fireEvent('over', dd, e, data);
20550         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20551             this.valid ? this.dropAllowed : this.dropNotAllowed
20552         );
20553     },
20554
20555     /**
20556      * @hide
20557      */
20558     notifyOut : function(dd, e, data)
20559     {
20560         this.fireEvent('out', dd, e, data);
20561         if(this.overClass){
20562             this.el.removeClass(this.overClass);
20563         }
20564     },
20565
20566     /**
20567      * @hide
20568      */
20569     notifyDrop : function(dd, e, data)
20570     {
20571         this.success = false;
20572         this.fireEvent('drop', dd, e, data);
20573         return this.success;
20574     }
20575 });/*
20576  * Based on:
20577  * Ext JS Library 1.1.1
20578  * Copyright(c) 2006-2007, Ext JS, LLC.
20579  *
20580  * Originally Released Under LGPL - original licence link has changed is not relivant.
20581  *
20582  * Fork - LGPL
20583  * <script type="text/javascript">
20584  */
20585
20586
20587 /**
20588  * @class Roo.dd.DragZone
20589  * @extends Roo.dd.DragSource
20590  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20591  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20592  * @constructor
20593  * @param {String/HTMLElement/Element} el The container element
20594  * @param {Object} config
20595  */
20596 Roo.dd.DragZone = function(el, config){
20597     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20598     if(this.containerScroll){
20599         Roo.dd.ScrollManager.register(this.el);
20600     }
20601 };
20602
20603 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20604     /**
20605      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20606      * for auto scrolling during drag operations.
20607      */
20608     /**
20609      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20610      * method after a failed drop (defaults to "c3daf9" - light blue)
20611      */
20612
20613     /**
20614      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20615      * for a valid target to drag based on the mouse down. Override this method
20616      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20617      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20618      * @param {EventObject} e The mouse down event
20619      * @return {Object} The dragData
20620      */
20621     getDragData : function(e){
20622         return Roo.dd.Registry.getHandleFromEvent(e);
20623     },
20624     
20625     /**
20626      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20627      * this.dragData.ddel
20628      * @param {Number} x The x position of the click on the dragged object
20629      * @param {Number} y The y position of the click on the dragged object
20630      * @return {Boolean} true to continue the drag, false to cancel
20631      */
20632     onInitDrag : function(x, y){
20633         this.proxy.update(this.dragData.ddel.cloneNode(true));
20634         this.onStartDrag(x, y);
20635         return true;
20636     },
20637     
20638     /**
20639      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20640      */
20641     afterRepair : function(){
20642         if(Roo.enableFx){
20643             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20644         }
20645         this.dragging = false;
20646     },
20647
20648     /**
20649      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20650      * the XY of this.dragData.ddel
20651      * @param {EventObject} e The mouse up event
20652      * @return {Array} The xy location (e.g. [100, 200])
20653      */
20654     getRepairXY : function(e){
20655         return Roo.Element.fly(this.dragData.ddel).getXY();  
20656     }
20657 });/*
20658  * Based on:
20659  * Ext JS Library 1.1.1
20660  * Copyright(c) 2006-2007, Ext JS, LLC.
20661  *
20662  * Originally Released Under LGPL - original licence link has changed is not relivant.
20663  *
20664  * Fork - LGPL
20665  * <script type="text/javascript">
20666  */
20667 /**
20668  * @class Roo.dd.DropZone
20669  * @extends Roo.dd.DropTarget
20670  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20671  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20672  * @constructor
20673  * @param {String/HTMLElement/Element} el The container element
20674  * @param {Object} config
20675  */
20676 Roo.dd.DropZone = function(el, config){
20677     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20678 };
20679
20680 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20681     /**
20682      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20683      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20684      * provide your own custom lookup.
20685      * @param {Event} e The event
20686      * @return {Object} data The custom data
20687      */
20688     getTargetFromEvent : function(e){
20689         return Roo.dd.Registry.getTargetFromEvent(e);
20690     },
20691
20692     /**
20693      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20694      * that it has registered.  This method has no default implementation and should be overridden to provide
20695      * node-specific processing if necessary.
20696      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20697      * {@link #getTargetFromEvent} for this node)
20698      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20699      * @param {Event} e The event
20700      * @param {Object} data An object containing arbitrary data supplied by the drag source
20701      */
20702     onNodeEnter : function(n, dd, e, data){
20703         
20704     },
20705
20706     /**
20707      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20708      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20709      * overridden to provide the proper feedback.
20710      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20711      * {@link #getTargetFromEvent} for this node)
20712      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20713      * @param {Event} e The event
20714      * @param {Object} data An object containing arbitrary data supplied by the drag source
20715      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20716      * underlying {@link Roo.dd.StatusProxy} can be updated
20717      */
20718     onNodeOver : function(n, dd, e, data){
20719         return this.dropAllowed;
20720     },
20721
20722     /**
20723      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20724      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20725      * node-specific processing if necessary.
20726      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20727      * {@link #getTargetFromEvent} for this node)
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag source
20731      */
20732     onNodeOut : function(n, dd, e, data){
20733         
20734     },
20735
20736     /**
20737      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20738      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20739      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20740      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20741      * {@link #getTargetFromEvent} for this node)
20742      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20743      * @param {Event} e The event
20744      * @param {Object} data An object containing arbitrary data supplied by the drag source
20745      * @return {Boolean} True if the drop was valid, else false
20746      */
20747     onNodeDrop : function(n, dd, e, data){
20748         return false;
20749     },
20750
20751     /**
20752      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20753      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20754      * it should be overridden to provide the proper feedback if necessary.
20755      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20756      * @param {Event} e The event
20757      * @param {Object} data An object containing arbitrary data supplied by the drag source
20758      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20759      * underlying {@link Roo.dd.StatusProxy} can be updated
20760      */
20761     onContainerOver : function(dd, e, data){
20762         return this.dropNotAllowed;
20763     },
20764
20765     /**
20766      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20767      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20768      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20769      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20770      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20771      * @param {Event} e The event
20772      * @param {Object} data An object containing arbitrary data supplied by the drag source
20773      * @return {Boolean} True if the drop was valid, else false
20774      */
20775     onContainerDrop : function(dd, e, data){
20776         return false;
20777     },
20778
20779     /**
20780      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20781      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20782      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20783      * you should override this method and provide a custom implementation.
20784      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20785      * @param {Event} e The event
20786      * @param {Object} data An object containing arbitrary data supplied by the drag source
20787      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20788      * underlying {@link Roo.dd.StatusProxy} can be updated
20789      */
20790     notifyEnter : function(dd, e, data){
20791         return this.dropNotAllowed;
20792     },
20793
20794     /**
20795      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20796      * This method will be called on every mouse movement while the drag source is over the drop zone.
20797      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20798      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20799      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20800      * registered node, it will call {@link #onContainerOver}.
20801      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20802      * @param {Event} e The event
20803      * @param {Object} data An object containing arbitrary data supplied by the drag source
20804      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20805      * underlying {@link Roo.dd.StatusProxy} can be updated
20806      */
20807     notifyOver : function(dd, e, data){
20808         var n = this.getTargetFromEvent(e);
20809         if(!n){ // not over valid drop target
20810             if(this.lastOverNode){
20811                 this.onNodeOut(this.lastOverNode, dd, e, data);
20812                 this.lastOverNode = null;
20813             }
20814             return this.onContainerOver(dd, e, data);
20815         }
20816         if(this.lastOverNode != n){
20817             if(this.lastOverNode){
20818                 this.onNodeOut(this.lastOverNode, dd, e, data);
20819             }
20820             this.onNodeEnter(n, dd, e, data);
20821             this.lastOverNode = n;
20822         }
20823         return this.onNodeOver(n, dd, e, data);
20824     },
20825
20826     /**
20827      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20828      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20829      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20830      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20831      * @param {Event} e The event
20832      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20833      */
20834     notifyOut : function(dd, e, data){
20835         if(this.lastOverNode){
20836             this.onNodeOut(this.lastOverNode, dd, e, data);
20837             this.lastOverNode = null;
20838         }
20839     },
20840
20841     /**
20842      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20843      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20844      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20845      * otherwise it will call {@link #onContainerDrop}.
20846      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20847      * @param {Event} e The event
20848      * @param {Object} data An object containing arbitrary data supplied by the drag source
20849      * @return {Boolean} True if the drop was valid, else false
20850      */
20851     notifyDrop : function(dd, e, data){
20852         if(this.lastOverNode){
20853             this.onNodeOut(this.lastOverNode, dd, e, data);
20854             this.lastOverNode = null;
20855         }
20856         var n = this.getTargetFromEvent(e);
20857         return n ?
20858             this.onNodeDrop(n, dd, e, data) :
20859             this.onContainerDrop(dd, e, data);
20860     },
20861
20862     // private
20863     triggerCacheRefresh : function(){
20864         Roo.dd.DDM.refreshCache(this.groups);
20865     }  
20866 });/*
20867  * Based on:
20868  * Ext JS Library 1.1.1
20869  * Copyright(c) 2006-2007, Ext JS, LLC.
20870  *
20871  * Originally Released Under LGPL - original licence link has changed is not relivant.
20872  *
20873  * Fork - LGPL
20874  * <script type="text/javascript">
20875  */
20876
20877
20878 /**
20879  * @class Roo.data.SortTypes
20880  * @singleton
20881  * Defines the default sorting (casting?) comparison functions used when sorting data.
20882  */
20883 Roo.data.SortTypes = {
20884     /**
20885      * Default sort that does nothing
20886      * @param {Mixed} s The value being converted
20887      * @return {Mixed} The comparison value
20888      */
20889     none : function(s){
20890         return s;
20891     },
20892     
20893     /**
20894      * The regular expression used to strip tags
20895      * @type {RegExp}
20896      * @property
20897      */
20898     stripTagsRE : /<\/?[^>]+>/gi,
20899     
20900     /**
20901      * Strips all HTML tags to sort on text only
20902      * @param {Mixed} s The value being converted
20903      * @return {String} The comparison value
20904      */
20905     asText : function(s){
20906         return String(s).replace(this.stripTagsRE, "");
20907     },
20908     
20909     /**
20910      * Strips all HTML tags to sort on text only - Case insensitive
20911      * @param {Mixed} s The value being converted
20912      * @return {String} The comparison value
20913      */
20914     asUCText : function(s){
20915         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20916     },
20917     
20918     /**
20919      * Case insensitive string
20920      * @param {Mixed} s The value being converted
20921      * @return {String} The comparison value
20922      */
20923     asUCString : function(s) {
20924         return String(s).toUpperCase();
20925     },
20926     
20927     /**
20928      * Date sorting
20929      * @param {Mixed} s The value being converted
20930      * @return {Number} The comparison value
20931      */
20932     asDate : function(s) {
20933         if(!s){
20934             return 0;
20935         }
20936         if(s instanceof Date){
20937             return s.getTime();
20938         }
20939         return Date.parse(String(s));
20940     },
20941     
20942     /**
20943      * Float sorting
20944      * @param {Mixed} s The value being converted
20945      * @return {Float} The comparison value
20946      */
20947     asFloat : function(s) {
20948         var val = parseFloat(String(s).replace(/,/g, ""));
20949         if(isNaN(val)) {
20950             val = 0;
20951         }
20952         return val;
20953     },
20954     
20955     /**
20956      * Integer sorting
20957      * @param {Mixed} s The value being converted
20958      * @return {Number} The comparison value
20959      */
20960     asInt : function(s) {
20961         var val = parseInt(String(s).replace(/,/g, ""));
20962         if(isNaN(val)) {
20963             val = 0;
20964         }
20965         return val;
20966     }
20967 };/*
20968  * Based on:
20969  * Ext JS Library 1.1.1
20970  * Copyright(c) 2006-2007, Ext JS, LLC.
20971  *
20972  * Originally Released Under LGPL - original licence link has changed is not relivant.
20973  *
20974  * Fork - LGPL
20975  * <script type="text/javascript">
20976  */
20977
20978 /**
20979 * @class Roo.data.Record
20980  * Instances of this class encapsulate both record <em>definition</em> information, and record
20981  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20982  * to access Records cached in an {@link Roo.data.Store} object.<br>
20983  * <p>
20984  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20985  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20986  * objects.<br>
20987  * <p>
20988  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20989  * @constructor
20990  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20991  * {@link #create}. The parameters are the same.
20992  * @param {Array} data An associative Array of data values keyed by the field name.
20993  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20994  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20995  * not specified an integer id is generated.
20996  */
20997 Roo.data.Record = function(data, id){
20998     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20999     this.data = data;
21000 };
21001
21002 /**
21003  * Generate a constructor for a specific record layout.
21004  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
21005  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
21006  * Each field definition object may contain the following properties: <ul>
21007  * <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,
21008  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
21009  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
21010  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
21011  * is being used, then this is a string containing the javascript expression to reference the data relative to 
21012  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
21013  * to the data item relative to the record element. If the mapping expression is the same as the field name,
21014  * this may be omitted.</p></li>
21015  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
21016  * <ul><li>auto (Default, implies no conversion)</li>
21017  * <li>string</li>
21018  * <li>int</li>
21019  * <li>float</li>
21020  * <li>boolean</li>
21021  * <li>date</li></ul></p></li>
21022  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
21023  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
21024  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
21025  * by the Reader into an object that will be stored in the Record. It is passed the
21026  * following parameters:<ul>
21027  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
21028  * </ul></p></li>
21029  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21030  * </ul>
21031  * <br>usage:<br><pre><code>
21032 var TopicRecord = Roo.data.Record.create(
21033     {name: 'title', mapping: 'topic_title'},
21034     {name: 'author', mapping: 'username'},
21035     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21036     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21037     {name: 'lastPoster', mapping: 'user2'},
21038     {name: 'excerpt', mapping: 'post_text'}
21039 );
21040
21041 var myNewRecord = new TopicRecord({
21042     title: 'Do my job please',
21043     author: 'noobie',
21044     totalPosts: 1,
21045     lastPost: new Date(),
21046     lastPoster: 'Animal',
21047     excerpt: 'No way dude!'
21048 });
21049 myStore.add(myNewRecord);
21050 </code></pre>
21051  * @method create
21052  * @static
21053  */
21054 Roo.data.Record.create = function(o){
21055     var f = function(){
21056         f.superclass.constructor.apply(this, arguments);
21057     };
21058     Roo.extend(f, Roo.data.Record);
21059     var p = f.prototype;
21060     p.fields = new Roo.util.MixedCollection(false, function(field){
21061         return field.name;
21062     });
21063     for(var i = 0, len = o.length; i < len; i++){
21064         p.fields.add(new Roo.data.Field(o[i]));
21065     }
21066     f.getField = function(name){
21067         return p.fields.get(name);  
21068     };
21069     return f;
21070 };
21071
21072 Roo.data.Record.AUTO_ID = 1000;
21073 Roo.data.Record.EDIT = 'edit';
21074 Roo.data.Record.REJECT = 'reject';
21075 Roo.data.Record.COMMIT = 'commit';
21076
21077 Roo.data.Record.prototype = {
21078     /**
21079      * Readonly flag - true if this record has been modified.
21080      * @type Boolean
21081      */
21082     dirty : false,
21083     editing : false,
21084     error: null,
21085     modified: null,
21086
21087     // private
21088     join : function(store){
21089         this.store = store;
21090     },
21091
21092     /**
21093      * Set the named field to the specified value.
21094      * @param {String} name The name of the field to set.
21095      * @param {Object} value The value to set the field to.
21096      */
21097     set : function(name, value){
21098         if(this.data[name] == value){
21099             return;
21100         }
21101         this.dirty = true;
21102         if(!this.modified){
21103             this.modified = {};
21104         }
21105         if(typeof this.modified[name] == 'undefined'){
21106             this.modified[name] = this.data[name];
21107         }
21108         this.data[name] = value;
21109         if(!this.editing && this.store){
21110             this.store.afterEdit(this);
21111         }       
21112     },
21113
21114     /**
21115      * Get the value of the named field.
21116      * @param {String} name The name of the field to get the value of.
21117      * @return {Object} The value of the field.
21118      */
21119     get : function(name){
21120         return this.data[name]; 
21121     },
21122
21123     // private
21124     beginEdit : function(){
21125         this.editing = true;
21126         this.modified = {}; 
21127     },
21128
21129     // private
21130     cancelEdit : function(){
21131         this.editing = false;
21132         delete this.modified;
21133     },
21134
21135     // private
21136     endEdit : function(){
21137         this.editing = false;
21138         if(this.dirty && this.store){
21139             this.store.afterEdit(this);
21140         }
21141     },
21142
21143     /**
21144      * Usually called by the {@link Roo.data.Store} which owns the Record.
21145      * Rejects all changes made to the Record since either creation, or the last commit operation.
21146      * Modified fields are reverted to their original values.
21147      * <p>
21148      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21149      * of reject operations.
21150      */
21151     reject : function(){
21152         var m = this.modified;
21153         for(var n in m){
21154             if(typeof m[n] != "function"){
21155                 this.data[n] = m[n];
21156             }
21157         }
21158         this.dirty = false;
21159         delete this.modified;
21160         this.editing = false;
21161         if(this.store){
21162             this.store.afterReject(this);
21163         }
21164     },
21165
21166     /**
21167      * Usually called by the {@link Roo.data.Store} which owns the Record.
21168      * Commits all changes made to the Record since either creation, or the last commit operation.
21169      * <p>
21170      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21171      * of commit operations.
21172      */
21173     commit : function(){
21174         this.dirty = false;
21175         delete this.modified;
21176         this.editing = false;
21177         if(this.store){
21178             this.store.afterCommit(this);
21179         }
21180     },
21181
21182     // private
21183     hasError : function(){
21184         return this.error != null;
21185     },
21186
21187     // private
21188     clearError : function(){
21189         this.error = null;
21190     },
21191
21192     /**
21193      * Creates a copy of this record.
21194      * @param {String} id (optional) A new record id if you don't want to use this record's id
21195      * @return {Record}
21196      */
21197     copy : function(newId) {
21198         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21199     }
21200 };/*
21201  * Based on:
21202  * Ext JS Library 1.1.1
21203  * Copyright(c) 2006-2007, Ext JS, LLC.
21204  *
21205  * Originally Released Under LGPL - original licence link has changed is not relivant.
21206  *
21207  * Fork - LGPL
21208  * <script type="text/javascript">
21209  */
21210
21211
21212
21213 /**
21214  * @class Roo.data.Store
21215  * @extends Roo.util.Observable
21216  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21217  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21218  * <p>
21219  * 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
21220  * has no knowledge of the format of the data returned by the Proxy.<br>
21221  * <p>
21222  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21223  * instances from the data object. These records are cached and made available through accessor functions.
21224  * @constructor
21225  * Creates a new Store.
21226  * @param {Object} config A config object containing the objects needed for the Store to access data,
21227  * and read the data into Records.
21228  */
21229 Roo.data.Store = function(config){
21230     this.data = new Roo.util.MixedCollection(false);
21231     this.data.getKey = function(o){
21232         return o.id;
21233     };
21234     this.baseParams = {};
21235     // private
21236     this.paramNames = {
21237         "start" : "start",
21238         "limit" : "limit",
21239         "sort" : "sort",
21240         "dir" : "dir",
21241         "multisort" : "_multisort"
21242     };
21243
21244     if(config && config.data){
21245         this.inlineData = config.data;
21246         delete config.data;
21247     }
21248
21249     Roo.apply(this, config);
21250     
21251     if(this.reader){ // reader passed
21252         this.reader = Roo.factory(this.reader, Roo.data);
21253         this.reader.xmodule = this.xmodule || false;
21254         if(!this.recordType){
21255             this.recordType = this.reader.recordType;
21256         }
21257         if(this.reader.onMetaChange){
21258             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21259         }
21260     }
21261
21262     if(this.recordType){
21263         this.fields = this.recordType.prototype.fields;
21264     }
21265     this.modified = [];
21266
21267     this.addEvents({
21268         /**
21269          * @event datachanged
21270          * Fires when the data cache has changed, and a widget which is using this Store
21271          * as a Record cache should refresh its view.
21272          * @param {Store} this
21273          */
21274         datachanged : true,
21275         /**
21276          * @event metachange
21277          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21278          * @param {Store} this
21279          * @param {Object} meta The JSON metadata
21280          */
21281         metachange : true,
21282         /**
21283          * @event add
21284          * Fires when Records have been added to the Store
21285          * @param {Store} this
21286          * @param {Roo.data.Record[]} records The array of Records added
21287          * @param {Number} index The index at which the record(s) were added
21288          */
21289         add : true,
21290         /**
21291          * @event remove
21292          * Fires when a Record has been removed from the Store
21293          * @param {Store} this
21294          * @param {Roo.data.Record} record The Record that was removed
21295          * @param {Number} index The index at which the record was removed
21296          */
21297         remove : true,
21298         /**
21299          * @event update
21300          * Fires when a Record has been updated
21301          * @param {Store} this
21302          * @param {Roo.data.Record} record The Record that was updated
21303          * @param {String} operation The update operation being performed.  Value may be one of:
21304          * <pre><code>
21305  Roo.data.Record.EDIT
21306  Roo.data.Record.REJECT
21307  Roo.data.Record.COMMIT
21308          * </code></pre>
21309          */
21310         update : true,
21311         /**
21312          * @event clear
21313          * Fires when the data cache has been cleared.
21314          * @param {Store} this
21315          */
21316         clear : true,
21317         /**
21318          * @event beforeload
21319          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21320          * the load action will be canceled.
21321          * @param {Store} this
21322          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21323          */
21324         beforeload : true,
21325         /**
21326          * @event beforeloadadd
21327          * Fires after a new set of Records has been loaded.
21328          * @param {Store} this
21329          * @param {Roo.data.Record[]} records The Records that were loaded
21330          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21331          */
21332         beforeloadadd : true,
21333         /**
21334          * @event load
21335          * Fires after a new set of Records has been loaded, before they are added to the store.
21336          * @param {Store} this
21337          * @param {Roo.data.Record[]} records The Records that were loaded
21338          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21339          * @params {Object} return from reader
21340          */
21341         load : true,
21342         /**
21343          * @event loadexception
21344          * Fires if an exception occurs in the Proxy during loading.
21345          * Called with the signature of the Proxy's "loadexception" event.
21346          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21347          * 
21348          * @param {Proxy} 
21349          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21350          * @param {Object} load options 
21351          * @param {Object} jsonData from your request (normally this contains the Exception)
21352          */
21353         loadexception : true
21354     });
21355     
21356     if(this.proxy){
21357         this.proxy = Roo.factory(this.proxy, Roo.data);
21358         this.proxy.xmodule = this.xmodule || false;
21359         this.relayEvents(this.proxy,  ["loadexception"]);
21360     }
21361     this.sortToggle = {};
21362     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21363
21364     Roo.data.Store.superclass.constructor.call(this);
21365
21366     if(this.inlineData){
21367         this.loadData(this.inlineData);
21368         delete this.inlineData;
21369     }
21370 };
21371
21372 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21373      /**
21374     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21375     * without a remote query - used by combo/forms at present.
21376     */
21377     
21378     /**
21379     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21380     */
21381     /**
21382     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21383     */
21384     /**
21385     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21386     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21387     */
21388     /**
21389     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21390     * on any HTTP request
21391     */
21392     /**
21393     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21394     */
21395     /**
21396     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21397     */
21398     multiSort: false,
21399     /**
21400     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21401     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21402     */
21403     remoteSort : false,
21404
21405     /**
21406     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21407      * loaded or when a record is removed. (defaults to false).
21408     */
21409     pruneModifiedRecords : false,
21410
21411     // private
21412     lastOptions : null,
21413
21414     /**
21415      * Add Records to the Store and fires the add event.
21416      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21417      */
21418     add : function(records){
21419         records = [].concat(records);
21420         for(var i = 0, len = records.length; i < len; i++){
21421             records[i].join(this);
21422         }
21423         var index = this.data.length;
21424         this.data.addAll(records);
21425         this.fireEvent("add", this, records, index);
21426     },
21427
21428     /**
21429      * Remove a Record from the Store and fires the remove event.
21430      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21431      */
21432     remove : function(record){
21433         var index = this.data.indexOf(record);
21434         this.data.removeAt(index);
21435         if(this.pruneModifiedRecords){
21436             this.modified.remove(record);
21437         }
21438         this.fireEvent("remove", this, record, index);
21439     },
21440
21441     /**
21442      * Remove all Records from the Store and fires the clear event.
21443      */
21444     removeAll : function(){
21445         this.data.clear();
21446         if(this.pruneModifiedRecords){
21447             this.modified = [];
21448         }
21449         this.fireEvent("clear", this);
21450     },
21451
21452     /**
21453      * Inserts Records to the Store at the given index and fires the add event.
21454      * @param {Number} index The start index at which to insert the passed Records.
21455      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21456      */
21457     insert : function(index, records){
21458         records = [].concat(records);
21459         for(var i = 0, len = records.length; i < len; i++){
21460             this.data.insert(index, records[i]);
21461             records[i].join(this);
21462         }
21463         this.fireEvent("add", this, records, index);
21464     },
21465
21466     /**
21467      * Get the index within the cache of the passed Record.
21468      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21469      * @return {Number} The index of the passed Record. Returns -1 if not found.
21470      */
21471     indexOf : function(record){
21472         return this.data.indexOf(record);
21473     },
21474
21475     /**
21476      * Get the index within the cache of the Record with the passed id.
21477      * @param {String} id The id of the Record to find.
21478      * @return {Number} The index of the Record. Returns -1 if not found.
21479      */
21480     indexOfId : function(id){
21481         return this.data.indexOfKey(id);
21482     },
21483
21484     /**
21485      * Get the Record with the specified id.
21486      * @param {String} id The id of the Record to find.
21487      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21488      */
21489     getById : function(id){
21490         return this.data.key(id);
21491     },
21492
21493     /**
21494      * Get the Record at the specified index.
21495      * @param {Number} index The index of the Record to find.
21496      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21497      */
21498     getAt : function(index){
21499         return this.data.itemAt(index);
21500     },
21501
21502     /**
21503      * Returns a range of Records between specified indices.
21504      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21505      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21506      * @return {Roo.data.Record[]} An array of Records
21507      */
21508     getRange : function(start, end){
21509         return this.data.getRange(start, end);
21510     },
21511
21512     // private
21513     storeOptions : function(o){
21514         o = Roo.apply({}, o);
21515         delete o.callback;
21516         delete o.scope;
21517         this.lastOptions = o;
21518     },
21519
21520     /**
21521      * Loads the Record cache from the configured Proxy using the configured Reader.
21522      * <p>
21523      * If using remote paging, then the first load call must specify the <em>start</em>
21524      * and <em>limit</em> properties in the options.params property to establish the initial
21525      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21526      * <p>
21527      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21528      * and this call will return before the new data has been loaded. Perform any post-processing
21529      * in a callback function, or in a "load" event handler.</strong>
21530      * <p>
21531      * @param {Object} options An object containing properties which control loading options:<ul>
21532      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21533      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21534      * passed the following arguments:<ul>
21535      * <li>r : Roo.data.Record[]</li>
21536      * <li>options: Options object from the load call</li>
21537      * <li>success: Boolean success indicator</li></ul></li>
21538      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21539      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21540      * </ul>
21541      */
21542     load : function(options){
21543         options = options || {};
21544         if(this.fireEvent("beforeload", this, options) !== false){
21545             this.storeOptions(options);
21546             var p = Roo.apply(options.params || {}, this.baseParams);
21547             // if meta was not loaded from remote source.. try requesting it.
21548             if (!this.reader.metaFromRemote) {
21549                 p._requestMeta = 1;
21550             }
21551             if(this.sortInfo && this.remoteSort){
21552                 var pn = this.paramNames;
21553                 p[pn["sort"]] = this.sortInfo.field;
21554                 p[pn["dir"]] = this.sortInfo.direction;
21555             }
21556             if (this.multiSort) {
21557                 var pn = this.paramNames;
21558                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21559             }
21560             
21561             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21562         }
21563     },
21564
21565     /**
21566      * Reloads the Record cache from the configured Proxy using the configured Reader and
21567      * the options from the last load operation performed.
21568      * @param {Object} options (optional) An object containing properties which may override the options
21569      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21570      * the most recently used options are reused).
21571      */
21572     reload : function(options){
21573         this.load(Roo.applyIf(options||{}, this.lastOptions));
21574     },
21575
21576     // private
21577     // Called as a callback by the Reader during a load operation.
21578     loadRecords : function(o, options, success){
21579         if(!o || success === false){
21580             if(success !== false){
21581                 this.fireEvent("load", this, [], options, o);
21582             }
21583             if(options.callback){
21584                 options.callback.call(options.scope || this, [], options, false);
21585             }
21586             return;
21587         }
21588         // if data returned failure - throw an exception.
21589         if (o.success === false) {
21590             // show a message if no listener is registered.
21591             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21592                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21593             }
21594             // loadmask wil be hooked into this..
21595             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21596             return;
21597         }
21598         var r = o.records, t = o.totalRecords || r.length;
21599         
21600         this.fireEvent("beforeloadadd", this, r, options, o);
21601         
21602         if(!options || options.add !== true){
21603             if(this.pruneModifiedRecords){
21604                 this.modified = [];
21605             }
21606             for(var i = 0, len = r.length; i < len; i++){
21607                 r[i].join(this);
21608             }
21609             if(this.snapshot){
21610                 this.data = this.snapshot;
21611                 delete this.snapshot;
21612             }
21613             this.data.clear();
21614             this.data.addAll(r);
21615             this.totalLength = t;
21616             this.applySort();
21617             this.fireEvent("datachanged", this);
21618         }else{
21619             this.totalLength = Math.max(t, this.data.length+r.length);
21620             this.add(r);
21621         }
21622         this.fireEvent("load", this, r, options, o);
21623         if(options.callback){
21624             options.callback.call(options.scope || this, r, options, true);
21625         }
21626     },
21627
21628
21629     /**
21630      * Loads data from a passed data block. A Reader which understands the format of the data
21631      * must have been configured in the constructor.
21632      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21633      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21634      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21635      */
21636     loadData : function(o, append){
21637         var r = this.reader.readRecords(o);
21638         this.loadRecords(r, {add: append}, true);
21639     },
21640
21641     /**
21642      * Gets the number of cached records.
21643      * <p>
21644      * <em>If using paging, this may not be the total size of the dataset. If the data object
21645      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21646      * the data set size</em>
21647      */
21648     getCount : function(){
21649         return this.data.length || 0;
21650     },
21651
21652     /**
21653      * Gets the total number of records in the dataset as returned by the server.
21654      * <p>
21655      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21656      * the dataset size</em>
21657      */
21658     getTotalCount : function(){
21659         return this.totalLength || 0;
21660     },
21661
21662     /**
21663      * Returns the sort state of the Store as an object with two properties:
21664      * <pre><code>
21665  field {String} The name of the field by which the Records are sorted
21666  direction {String} The sort order, "ASC" or "DESC"
21667      * </code></pre>
21668      */
21669     getSortState : function(){
21670         return this.sortInfo;
21671     },
21672
21673     // private
21674     applySort : function(){
21675         if(this.sortInfo && !this.remoteSort){
21676             var s = this.sortInfo, f = s.field;
21677             var st = this.fields.get(f).sortType;
21678             var fn = function(r1, r2){
21679                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21680                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21681             };
21682             this.data.sort(s.direction, fn);
21683             if(this.snapshot && this.snapshot != this.data){
21684                 this.snapshot.sort(s.direction, fn);
21685             }
21686         }
21687     },
21688
21689     /**
21690      * Sets the default sort column and order to be used by the next load operation.
21691      * @param {String} fieldName The name of the field to sort by.
21692      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21693      */
21694     setDefaultSort : function(field, dir){
21695         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21696     },
21697
21698     /**
21699      * Sort the Records.
21700      * If remote sorting is used, the sort is performed on the server, and the cache is
21701      * reloaded. If local sorting is used, the cache is sorted internally.
21702      * @param {String} fieldName The name of the field to sort by.
21703      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21704      */
21705     sort : function(fieldName, dir){
21706         var f = this.fields.get(fieldName);
21707         if(!dir){
21708             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21709             
21710             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21711                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21712             }else{
21713                 dir = f.sortDir;
21714             }
21715         }
21716         this.sortToggle[f.name] = dir;
21717         this.sortInfo = {field: f.name, direction: dir};
21718         if(!this.remoteSort){
21719             this.applySort();
21720             this.fireEvent("datachanged", this);
21721         }else{
21722             this.load(this.lastOptions);
21723         }
21724     },
21725
21726     /**
21727      * Calls the specified function for each of the Records in the cache.
21728      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21729      * Returning <em>false</em> aborts and exits the iteration.
21730      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21731      */
21732     each : function(fn, scope){
21733         this.data.each(fn, scope);
21734     },
21735
21736     /**
21737      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21738      * (e.g., during paging).
21739      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21740      */
21741     getModifiedRecords : function(){
21742         return this.modified;
21743     },
21744
21745     // private
21746     createFilterFn : function(property, value, anyMatch){
21747         if(!value.exec){ // not a regex
21748             value = String(value);
21749             if(value.length == 0){
21750                 return false;
21751             }
21752             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21753         }
21754         return function(r){
21755             return value.test(r.data[property]);
21756         };
21757     },
21758
21759     /**
21760      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21761      * @param {String} property A field on your records
21762      * @param {Number} start The record index to start at (defaults to 0)
21763      * @param {Number} end The last record index to include (defaults to length - 1)
21764      * @return {Number} The sum
21765      */
21766     sum : function(property, start, end){
21767         var rs = this.data.items, v = 0;
21768         start = start || 0;
21769         end = (end || end === 0) ? end : rs.length-1;
21770
21771         for(var i = start; i <= end; i++){
21772             v += (rs[i].data[property] || 0);
21773         }
21774         return v;
21775     },
21776
21777     /**
21778      * Filter the records by a specified property.
21779      * @param {String} field A field on your records
21780      * @param {String/RegExp} value Either a string that the field
21781      * should start with or a RegExp to test against the field
21782      * @param {Boolean} anyMatch True to match any part not just the beginning
21783      */
21784     filter : function(property, value, anyMatch){
21785         var fn = this.createFilterFn(property, value, anyMatch);
21786         return fn ? this.filterBy(fn) : this.clearFilter();
21787     },
21788
21789     /**
21790      * Filter by a function. The specified function will be called with each
21791      * record in this data source. If the function returns true the record is included,
21792      * otherwise it is filtered.
21793      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21794      * @param {Object} scope (optional) The scope of the function (defaults to this)
21795      */
21796     filterBy : function(fn, scope){
21797         this.snapshot = this.snapshot || this.data;
21798         this.data = this.queryBy(fn, scope||this);
21799         this.fireEvent("datachanged", this);
21800     },
21801
21802     /**
21803      * Query the records by a specified property.
21804      * @param {String} field A field on your records
21805      * @param {String/RegExp} value Either a string that the field
21806      * should start with or a RegExp to test against the field
21807      * @param {Boolean} anyMatch True to match any part not just the beginning
21808      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21809      */
21810     query : function(property, value, anyMatch){
21811         var fn = this.createFilterFn(property, value, anyMatch);
21812         return fn ? this.queryBy(fn) : this.data.clone();
21813     },
21814
21815     /**
21816      * Query by a function. The specified function will be called with each
21817      * record in this data source. If the function returns true the record is included
21818      * in the results.
21819      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21820      * @param {Object} scope (optional) The scope of the function (defaults to this)
21821       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21822      **/
21823     queryBy : function(fn, scope){
21824         var data = this.snapshot || this.data;
21825         return data.filterBy(fn, scope||this);
21826     },
21827
21828     /**
21829      * Collects unique values for a particular dataIndex from this store.
21830      * @param {String} dataIndex The property to collect
21831      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21832      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21833      * @return {Array} An array of the unique values
21834      **/
21835     collect : function(dataIndex, allowNull, bypassFilter){
21836         var d = (bypassFilter === true && this.snapshot) ?
21837                 this.snapshot.items : this.data.items;
21838         var v, sv, r = [], l = {};
21839         for(var i = 0, len = d.length; i < len; i++){
21840             v = d[i].data[dataIndex];
21841             sv = String(v);
21842             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21843                 l[sv] = true;
21844                 r[r.length] = v;
21845             }
21846         }
21847         return r;
21848     },
21849
21850     /**
21851      * Revert to a view of the Record cache with no filtering applied.
21852      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21853      */
21854     clearFilter : function(suppressEvent){
21855         if(this.snapshot && this.snapshot != this.data){
21856             this.data = this.snapshot;
21857             delete this.snapshot;
21858             if(suppressEvent !== true){
21859                 this.fireEvent("datachanged", this);
21860             }
21861         }
21862     },
21863
21864     // private
21865     afterEdit : function(record){
21866         if(this.modified.indexOf(record) == -1){
21867             this.modified.push(record);
21868         }
21869         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21870     },
21871     
21872     // private
21873     afterReject : function(record){
21874         this.modified.remove(record);
21875         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21876     },
21877
21878     // private
21879     afterCommit : function(record){
21880         this.modified.remove(record);
21881         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21882     },
21883
21884     /**
21885      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21886      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21887      */
21888     commitChanges : function(){
21889         var m = this.modified.slice(0);
21890         this.modified = [];
21891         for(var i = 0, len = m.length; i < len; i++){
21892             m[i].commit();
21893         }
21894     },
21895
21896     /**
21897      * Cancel outstanding changes on all changed records.
21898      */
21899     rejectChanges : function(){
21900         var m = this.modified.slice(0);
21901         this.modified = [];
21902         for(var i = 0, len = m.length; i < len; i++){
21903             m[i].reject();
21904         }
21905     },
21906
21907     onMetaChange : function(meta, rtype, o){
21908         this.recordType = rtype;
21909         this.fields = rtype.prototype.fields;
21910         delete this.snapshot;
21911         this.sortInfo = meta.sortInfo || this.sortInfo;
21912         this.modified = [];
21913         this.fireEvent('metachange', this, this.reader.meta);
21914     },
21915     
21916     moveIndex : function(data, type)
21917     {
21918         var index = this.indexOf(data);
21919         
21920         var newIndex = index + type;
21921         
21922         this.remove(data);
21923         
21924         this.insert(newIndex, data);
21925         
21926     }
21927 });/*
21928  * Based on:
21929  * Ext JS Library 1.1.1
21930  * Copyright(c) 2006-2007, Ext JS, LLC.
21931  *
21932  * Originally Released Under LGPL - original licence link has changed is not relivant.
21933  *
21934  * Fork - LGPL
21935  * <script type="text/javascript">
21936  */
21937
21938 /**
21939  * @class Roo.data.SimpleStore
21940  * @extends Roo.data.Store
21941  * Small helper class to make creating Stores from Array data easier.
21942  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21943  * @cfg {Array} fields An array of field definition objects, or field name strings.
21944  * @cfg {Array} data The multi-dimensional array of data
21945  * @constructor
21946  * @param {Object} config
21947  */
21948 Roo.data.SimpleStore = function(config){
21949     Roo.data.SimpleStore.superclass.constructor.call(this, {
21950         isLocal : true,
21951         reader: new Roo.data.ArrayReader({
21952                 id: config.id
21953             },
21954             Roo.data.Record.create(config.fields)
21955         ),
21956         proxy : new Roo.data.MemoryProxy(config.data)
21957     });
21958     this.load();
21959 };
21960 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21961  * Based on:
21962  * Ext JS Library 1.1.1
21963  * Copyright(c) 2006-2007, Ext JS, LLC.
21964  *
21965  * Originally Released Under LGPL - original licence link has changed is not relivant.
21966  *
21967  * Fork - LGPL
21968  * <script type="text/javascript">
21969  */
21970
21971 /**
21972 /**
21973  * @extends Roo.data.Store
21974  * @class Roo.data.JsonStore
21975  * Small helper class to make creating Stores for JSON data easier. <br/>
21976 <pre><code>
21977 var store = new Roo.data.JsonStore({
21978     url: 'get-images.php',
21979     root: 'images',
21980     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21981 });
21982 </code></pre>
21983  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21984  * JsonReader and HttpProxy (unless inline data is provided).</b>
21985  * @cfg {Array} fields An array of field definition objects, or field name strings.
21986  * @constructor
21987  * @param {Object} config
21988  */
21989 Roo.data.JsonStore = function(c){
21990     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21991         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21992         reader: new Roo.data.JsonReader(c, c.fields)
21993     }));
21994 };
21995 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21996  * Based on:
21997  * Ext JS Library 1.1.1
21998  * Copyright(c) 2006-2007, Ext JS, LLC.
21999  *
22000  * Originally Released Under LGPL - original licence link has changed is not relivant.
22001  *
22002  * Fork - LGPL
22003  * <script type="text/javascript">
22004  */
22005
22006  
22007 Roo.data.Field = function(config){
22008     if(typeof config == "string"){
22009         config = {name: config};
22010     }
22011     Roo.apply(this, config);
22012     
22013     if(!this.type){
22014         this.type = "auto";
22015     }
22016     
22017     var st = Roo.data.SortTypes;
22018     // named sortTypes are supported, here we look them up
22019     if(typeof this.sortType == "string"){
22020         this.sortType = st[this.sortType];
22021     }
22022     
22023     // set default sortType for strings and dates
22024     if(!this.sortType){
22025         switch(this.type){
22026             case "string":
22027                 this.sortType = st.asUCString;
22028                 break;
22029             case "date":
22030                 this.sortType = st.asDate;
22031                 break;
22032             default:
22033                 this.sortType = st.none;
22034         }
22035     }
22036
22037     // define once
22038     var stripRe = /[\$,%]/g;
22039
22040     // prebuilt conversion function for this field, instead of
22041     // switching every time we're reading a value
22042     if(!this.convert){
22043         var cv, dateFormat = this.dateFormat;
22044         switch(this.type){
22045             case "":
22046             case "auto":
22047             case undefined:
22048                 cv = function(v){ return v; };
22049                 break;
22050             case "string":
22051                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22052                 break;
22053             case "int":
22054                 cv = function(v){
22055                     return v !== undefined && v !== null && v !== '' ?
22056                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22057                     };
22058                 break;
22059             case "float":
22060                 cv = function(v){
22061                     return v !== undefined && v !== null && v !== '' ?
22062                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22063                     };
22064                 break;
22065             case "bool":
22066             case "boolean":
22067                 cv = function(v){ return v === true || v === "true" || v == 1; };
22068                 break;
22069             case "date":
22070                 cv = function(v){
22071                     if(!v){
22072                         return '';
22073                     }
22074                     if(v instanceof Date){
22075                         return v;
22076                     }
22077                     if(dateFormat){
22078                         if(dateFormat == "timestamp"){
22079                             return new Date(v*1000);
22080                         }
22081                         return Date.parseDate(v, dateFormat);
22082                     }
22083                     var parsed = Date.parse(v);
22084                     return parsed ? new Date(parsed) : null;
22085                 };
22086              break;
22087             
22088         }
22089         this.convert = cv;
22090     }
22091 };
22092
22093 Roo.data.Field.prototype = {
22094     dateFormat: null,
22095     defaultValue: "",
22096     mapping: null,
22097     sortType : null,
22098     sortDir : "ASC"
22099 };/*
22100  * Based on:
22101  * Ext JS Library 1.1.1
22102  * Copyright(c) 2006-2007, Ext JS, LLC.
22103  *
22104  * Originally Released Under LGPL - original licence link has changed is not relivant.
22105  *
22106  * Fork - LGPL
22107  * <script type="text/javascript">
22108  */
22109  
22110 // Base class for reading structured data from a data source.  This class is intended to be
22111 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22112
22113 /**
22114  * @class Roo.data.DataReader
22115  * Base class for reading structured data from a data source.  This class is intended to be
22116  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22117  */
22118
22119 Roo.data.DataReader = function(meta, recordType){
22120     
22121     this.meta = meta;
22122     
22123     this.recordType = recordType instanceof Array ? 
22124         Roo.data.Record.create(recordType) : recordType;
22125 };
22126
22127 Roo.data.DataReader.prototype = {
22128      /**
22129      * Create an empty record
22130      * @param {Object} data (optional) - overlay some values
22131      * @return {Roo.data.Record} record created.
22132      */
22133     newRow :  function(d) {
22134         var da =  {};
22135         this.recordType.prototype.fields.each(function(c) {
22136             switch( c.type) {
22137                 case 'int' : da[c.name] = 0; break;
22138                 case 'date' : da[c.name] = new Date(); break;
22139                 case 'float' : da[c.name] = 0.0; break;
22140                 case 'boolean' : da[c.name] = false; break;
22141                 default : da[c.name] = ""; break;
22142             }
22143             
22144         });
22145         return new this.recordType(Roo.apply(da, d));
22146     }
22147     
22148 };/*
22149  * Based on:
22150  * Ext JS Library 1.1.1
22151  * Copyright(c) 2006-2007, Ext JS, LLC.
22152  *
22153  * Originally Released Under LGPL - original licence link has changed is not relivant.
22154  *
22155  * Fork - LGPL
22156  * <script type="text/javascript">
22157  */
22158
22159 /**
22160  * @class Roo.data.DataProxy
22161  * @extends Roo.data.Observable
22162  * This class is an abstract base class for implementations which provide retrieval of
22163  * unformatted data objects.<br>
22164  * <p>
22165  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22166  * (of the appropriate type which knows how to parse the data object) to provide a block of
22167  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22168  * <p>
22169  * Custom implementations must implement the load method as described in
22170  * {@link Roo.data.HttpProxy#load}.
22171  */
22172 Roo.data.DataProxy = function(){
22173     this.addEvents({
22174         /**
22175          * @event beforeload
22176          * Fires before a network request is made to retrieve a data object.
22177          * @param {Object} This DataProxy object.
22178          * @param {Object} params The params parameter to the load function.
22179          */
22180         beforeload : true,
22181         /**
22182          * @event load
22183          * Fires before the load method's callback is called.
22184          * @param {Object} This DataProxy object.
22185          * @param {Object} o The data object.
22186          * @param {Object} arg The callback argument object passed to the load function.
22187          */
22188         load : true,
22189         /**
22190          * @event loadexception
22191          * Fires if an Exception occurs during data retrieval.
22192          * @param {Object} This DataProxy object.
22193          * @param {Object} o The data object.
22194          * @param {Object} arg The callback argument object passed to the load function.
22195          * @param {Object} e The Exception.
22196          */
22197         loadexception : true
22198     });
22199     Roo.data.DataProxy.superclass.constructor.call(this);
22200 };
22201
22202 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22203
22204     /**
22205      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22206      */
22207 /*
22208  * Based on:
22209  * Ext JS Library 1.1.1
22210  * Copyright(c) 2006-2007, Ext JS, LLC.
22211  *
22212  * Originally Released Under LGPL - original licence link has changed is not relivant.
22213  *
22214  * Fork - LGPL
22215  * <script type="text/javascript">
22216  */
22217 /**
22218  * @class Roo.data.MemoryProxy
22219  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22220  * to the Reader when its load method is called.
22221  * @constructor
22222  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22223  */
22224 Roo.data.MemoryProxy = function(data){
22225     if (data.data) {
22226         data = data.data;
22227     }
22228     Roo.data.MemoryProxy.superclass.constructor.call(this);
22229     this.data = data;
22230 };
22231
22232 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22233     /**
22234      * Load data from the requested source (in this case an in-memory
22235      * data object passed to the constructor), read the data object into
22236      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22237      * process that block using the passed callback.
22238      * @param {Object} params This parameter is not used by the MemoryProxy class.
22239      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22240      * object into a block of Roo.data.Records.
22241      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22242      * The function must be passed <ul>
22243      * <li>The Record block object</li>
22244      * <li>The "arg" argument from the load function</li>
22245      * <li>A boolean success indicator</li>
22246      * </ul>
22247      * @param {Object} scope The scope in which to call the callback
22248      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22249      */
22250     load : function(params, reader, callback, scope, arg){
22251         params = params || {};
22252         var result;
22253         try {
22254             result = reader.readRecords(this.data);
22255         }catch(e){
22256             this.fireEvent("loadexception", this, arg, null, e);
22257             callback.call(scope, null, arg, false);
22258             return;
22259         }
22260         callback.call(scope, result, arg, true);
22261     },
22262     
22263     // private
22264     update : function(params, records){
22265         
22266     }
22267 });/*
22268  * Based on:
22269  * Ext JS Library 1.1.1
22270  * Copyright(c) 2006-2007, Ext JS, LLC.
22271  *
22272  * Originally Released Under LGPL - original licence link has changed is not relivant.
22273  *
22274  * Fork - LGPL
22275  * <script type="text/javascript">
22276  */
22277 /**
22278  * @class Roo.data.HttpProxy
22279  * @extends Roo.data.DataProxy
22280  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22281  * configured to reference a certain URL.<br><br>
22282  * <p>
22283  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22284  * from which the running page was served.<br><br>
22285  * <p>
22286  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22287  * <p>
22288  * Be aware that to enable the browser to parse an XML document, the server must set
22289  * the Content-Type header in the HTTP response to "text/xml".
22290  * @constructor
22291  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22292  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22293  * will be used to make the request.
22294  */
22295 Roo.data.HttpProxy = function(conn){
22296     Roo.data.HttpProxy.superclass.constructor.call(this);
22297     // is conn a conn config or a real conn?
22298     this.conn = conn;
22299     this.useAjax = !conn || !conn.events;
22300   
22301 };
22302
22303 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22304     // thse are take from connection...
22305     
22306     /**
22307      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22308      */
22309     /**
22310      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22311      * extra parameters to each request made by this object. (defaults to undefined)
22312      */
22313     /**
22314      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22315      *  to each request made by this object. (defaults to undefined)
22316      */
22317     /**
22318      * @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)
22319      */
22320     /**
22321      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22322      */
22323      /**
22324      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22325      * @type Boolean
22326      */
22327   
22328
22329     /**
22330      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22331      * @type Boolean
22332      */
22333     /**
22334      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22335      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22336      * a finer-grained basis than the DataProxy events.
22337      */
22338     getConnection : function(){
22339         return this.useAjax ? Roo.Ajax : this.conn;
22340     },
22341
22342     /**
22343      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22344      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22345      * process that block using the passed callback.
22346      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22347      * for the request to the remote server.
22348      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22349      * object into a block of Roo.data.Records.
22350      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22351      * The function must be passed <ul>
22352      * <li>The Record block object</li>
22353      * <li>The "arg" argument from the load function</li>
22354      * <li>A boolean success indicator</li>
22355      * </ul>
22356      * @param {Object} scope The scope in which to call the callback
22357      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22358      */
22359     load : function(params, reader, callback, scope, arg){
22360         if(this.fireEvent("beforeload", this, params) !== false){
22361             var  o = {
22362                 params : params || {},
22363                 request: {
22364                     callback : callback,
22365                     scope : scope,
22366                     arg : arg
22367                 },
22368                 reader: reader,
22369                 callback : this.loadResponse,
22370                 scope: this
22371             };
22372             if(this.useAjax){
22373                 Roo.applyIf(o, this.conn);
22374                 if(this.activeRequest){
22375                     Roo.Ajax.abort(this.activeRequest);
22376                 }
22377                 this.activeRequest = Roo.Ajax.request(o);
22378             }else{
22379                 this.conn.request(o);
22380             }
22381         }else{
22382             callback.call(scope||this, null, arg, false);
22383         }
22384     },
22385
22386     // private
22387     loadResponse : function(o, success, response){
22388         delete this.activeRequest;
22389         if(!success){
22390             this.fireEvent("loadexception", this, o, response);
22391             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22392             return;
22393         }
22394         var result;
22395         try {
22396             result = o.reader.read(response);
22397         }catch(e){
22398             this.fireEvent("loadexception", this, o, response, e);
22399             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22400             return;
22401         }
22402         
22403         this.fireEvent("load", this, o, o.request.arg);
22404         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22405     },
22406
22407     // private
22408     update : function(dataSet){
22409
22410     },
22411
22412     // private
22413     updateResponse : function(dataSet){
22414
22415     }
22416 });/*
22417  * Based on:
22418  * Ext JS Library 1.1.1
22419  * Copyright(c) 2006-2007, Ext JS, LLC.
22420  *
22421  * Originally Released Under LGPL - original licence link has changed is not relivant.
22422  *
22423  * Fork - LGPL
22424  * <script type="text/javascript">
22425  */
22426
22427 /**
22428  * @class Roo.data.ScriptTagProxy
22429  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22430  * other than the originating domain of the running page.<br><br>
22431  * <p>
22432  * <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
22433  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22434  * <p>
22435  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22436  * source code that is used as the source inside a &lt;script> tag.<br><br>
22437  * <p>
22438  * In order for the browser to process the returned data, the server must wrap the data object
22439  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22440  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22441  * depending on whether the callback name was passed:
22442  * <p>
22443  * <pre><code>
22444 boolean scriptTag = false;
22445 String cb = request.getParameter("callback");
22446 if (cb != null) {
22447     scriptTag = true;
22448     response.setContentType("text/javascript");
22449 } else {
22450     response.setContentType("application/x-json");
22451 }
22452 Writer out = response.getWriter();
22453 if (scriptTag) {
22454     out.write(cb + "(");
22455 }
22456 out.print(dataBlock.toJsonString());
22457 if (scriptTag) {
22458     out.write(");");
22459 }
22460 </pre></code>
22461  *
22462  * @constructor
22463  * @param {Object} config A configuration object.
22464  */
22465 Roo.data.ScriptTagProxy = function(config){
22466     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22467     Roo.apply(this, config);
22468     this.head = document.getElementsByTagName("head")[0];
22469 };
22470
22471 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22472
22473 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22474     /**
22475      * @cfg {String} url The URL from which to request the data object.
22476      */
22477     /**
22478      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22479      */
22480     timeout : 30000,
22481     /**
22482      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22483      * the server the name of the callback function set up by the load call to process the returned data object.
22484      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22485      * javascript output which calls this named function passing the data object as its only parameter.
22486      */
22487     callbackParam : "callback",
22488     /**
22489      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22490      * name to the request.
22491      */
22492     nocache : true,
22493
22494     /**
22495      * Load data from the configured URL, read the data object into
22496      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22497      * process that block using the passed callback.
22498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22499      * for the request to the remote server.
22500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22501      * object into a block of Roo.data.Records.
22502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22503      * The function must be passed <ul>
22504      * <li>The Record block object</li>
22505      * <li>The "arg" argument from the load function</li>
22506      * <li>A boolean success indicator</li>
22507      * </ul>
22508      * @param {Object} scope The scope in which to call the callback
22509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22510      */
22511     load : function(params, reader, callback, scope, arg){
22512         if(this.fireEvent("beforeload", this, params) !== false){
22513
22514             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22515
22516             var url = this.url;
22517             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22518             if(this.nocache){
22519                 url += "&_dc=" + (new Date().getTime());
22520             }
22521             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22522             var trans = {
22523                 id : transId,
22524                 cb : "stcCallback"+transId,
22525                 scriptId : "stcScript"+transId,
22526                 params : params,
22527                 arg : arg,
22528                 url : url,
22529                 callback : callback,
22530                 scope : scope,
22531                 reader : reader
22532             };
22533             var conn = this;
22534
22535             window[trans.cb] = function(o){
22536                 conn.handleResponse(o, trans);
22537             };
22538
22539             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22540
22541             if(this.autoAbort !== false){
22542                 this.abort();
22543             }
22544
22545             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22546
22547             var script = document.createElement("script");
22548             script.setAttribute("src", url);
22549             script.setAttribute("type", "text/javascript");
22550             script.setAttribute("id", trans.scriptId);
22551             this.head.appendChild(script);
22552
22553             this.trans = trans;
22554         }else{
22555             callback.call(scope||this, null, arg, false);
22556         }
22557     },
22558
22559     // private
22560     isLoading : function(){
22561         return this.trans ? true : false;
22562     },
22563
22564     /**
22565      * Abort the current server request.
22566      */
22567     abort : function(){
22568         if(this.isLoading()){
22569             this.destroyTrans(this.trans);
22570         }
22571     },
22572
22573     // private
22574     destroyTrans : function(trans, isLoaded){
22575         this.head.removeChild(document.getElementById(trans.scriptId));
22576         clearTimeout(trans.timeoutId);
22577         if(isLoaded){
22578             window[trans.cb] = undefined;
22579             try{
22580                 delete window[trans.cb];
22581             }catch(e){}
22582         }else{
22583             // if hasn't been loaded, wait for load to remove it to prevent script error
22584             window[trans.cb] = function(){
22585                 window[trans.cb] = undefined;
22586                 try{
22587                     delete window[trans.cb];
22588                 }catch(e){}
22589             };
22590         }
22591     },
22592
22593     // private
22594     handleResponse : function(o, trans){
22595         this.trans = false;
22596         this.destroyTrans(trans, true);
22597         var result;
22598         try {
22599             result = trans.reader.readRecords(o);
22600         }catch(e){
22601             this.fireEvent("loadexception", this, o, trans.arg, e);
22602             trans.callback.call(trans.scope||window, null, trans.arg, false);
22603             return;
22604         }
22605         this.fireEvent("load", this, o, trans.arg);
22606         trans.callback.call(trans.scope||window, result, trans.arg, true);
22607     },
22608
22609     // private
22610     handleFailure : function(trans){
22611         this.trans = false;
22612         this.destroyTrans(trans, false);
22613         this.fireEvent("loadexception", this, null, trans.arg);
22614         trans.callback.call(trans.scope||window, null, trans.arg, false);
22615     }
22616 });/*
22617  * Based on:
22618  * Ext JS Library 1.1.1
22619  * Copyright(c) 2006-2007, Ext JS, LLC.
22620  *
22621  * Originally Released Under LGPL - original licence link has changed is not relivant.
22622  *
22623  * Fork - LGPL
22624  * <script type="text/javascript">
22625  */
22626
22627 /**
22628  * @class Roo.data.JsonReader
22629  * @extends Roo.data.DataReader
22630  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22631  * based on mappings in a provided Roo.data.Record constructor.
22632  * 
22633  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22634  * in the reply previously. 
22635  * 
22636  * <p>
22637  * Example code:
22638  * <pre><code>
22639 var RecordDef = Roo.data.Record.create([
22640     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22641     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22642 ]);
22643 var myReader = new Roo.data.JsonReader({
22644     totalProperty: "results",    // The property which contains the total dataset size (optional)
22645     root: "rows",                // The property which contains an Array of row objects
22646     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22647 }, RecordDef);
22648 </code></pre>
22649  * <p>
22650  * This would consume a JSON file like this:
22651  * <pre><code>
22652 { 'results': 2, 'rows': [
22653     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22654     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22655 }
22656 </code></pre>
22657  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22658  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22659  * paged from the remote server.
22660  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22661  * @cfg {String} root name of the property which contains the Array of row objects.
22662  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22663  * @cfg {Array} fields Array of field definition objects
22664  * @constructor
22665  * Create a new JsonReader
22666  * @param {Object} meta Metadata configuration options
22667  * @param {Object} recordType Either an Array of field definition objects,
22668  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22669  */
22670 Roo.data.JsonReader = function(meta, recordType){
22671     
22672     meta = meta || {};
22673     // set some defaults:
22674     Roo.applyIf(meta, {
22675         totalProperty: 'total',
22676         successProperty : 'success',
22677         root : 'data',
22678         id : 'id'
22679     });
22680     
22681     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22682 };
22683 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22684     
22685     /**
22686      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22687      * Used by Store query builder to append _requestMeta to params.
22688      * 
22689      */
22690     metaFromRemote : false,
22691     /**
22692      * This method is only used by a DataProxy which has retrieved data from a remote server.
22693      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22694      * @return {Object} data A data block which is used by an Roo.data.Store object as
22695      * a cache of Roo.data.Records.
22696      */
22697     read : function(response){
22698         var json = response.responseText;
22699        
22700         var o = /* eval:var:o */ eval("("+json+")");
22701         if(!o) {
22702             throw {message: "JsonReader.read: Json object not found"};
22703         }
22704         
22705         if(o.metaData){
22706             
22707             delete this.ef;
22708             this.metaFromRemote = true;
22709             this.meta = o.metaData;
22710             this.recordType = Roo.data.Record.create(o.metaData.fields);
22711             this.onMetaChange(this.meta, this.recordType, o);
22712         }
22713         return this.readRecords(o);
22714     },
22715
22716     // private function a store will implement
22717     onMetaChange : function(meta, recordType, o){
22718
22719     },
22720
22721     /**
22722          * @ignore
22723          */
22724     simpleAccess: function(obj, subsc) {
22725         return obj[subsc];
22726     },
22727
22728         /**
22729          * @ignore
22730          */
22731     getJsonAccessor: function(){
22732         var re = /[\[\.]/;
22733         return function(expr) {
22734             try {
22735                 return(re.test(expr))
22736                     ? new Function("obj", "return obj." + expr)
22737                     : function(obj){
22738                         return obj[expr];
22739                     };
22740             } catch(e){}
22741             return Roo.emptyFn;
22742         };
22743     }(),
22744
22745     /**
22746      * Create a data block containing Roo.data.Records from an XML document.
22747      * @param {Object} o An object which contains an Array of row objects in the property specified
22748      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22749      * which contains the total size of the dataset.
22750      * @return {Object} data A data block which is used by an Roo.data.Store object as
22751      * a cache of Roo.data.Records.
22752      */
22753     readRecords : function(o){
22754         /**
22755          * After any data loads, the raw JSON data is available for further custom processing.
22756          * @type Object
22757          */
22758         this.o = o;
22759         var s = this.meta, Record = this.recordType,
22760             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22761
22762 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22763         if (!this.ef) {
22764             if(s.totalProperty) {
22765                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22766                 }
22767                 if(s.successProperty) {
22768                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22769                 }
22770                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22771                 if (s.id) {
22772                         var g = this.getJsonAccessor(s.id);
22773                         this.getId = function(rec) {
22774                                 var r = g(rec);  
22775                                 return (r === undefined || r === "") ? null : r;
22776                         };
22777                 } else {
22778                         this.getId = function(){return null;};
22779                 }
22780             this.ef = [];
22781             for(var jj = 0; jj < fl; jj++){
22782                 f = fi[jj];
22783                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22784                 this.ef[jj] = this.getJsonAccessor(map);
22785             }
22786         }
22787
22788         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22789         if(s.totalProperty){
22790             var vt = parseInt(this.getTotal(o), 10);
22791             if(!isNaN(vt)){
22792                 totalRecords = vt;
22793             }
22794         }
22795         if(s.successProperty){
22796             var vs = this.getSuccess(o);
22797             if(vs === false || vs === 'false'){
22798                 success = false;
22799             }
22800         }
22801         var records = [];
22802         for(var i = 0; i < c; i++){
22803                 var n = root[i];
22804             var values = {};
22805             var id = this.getId(n);
22806             for(var j = 0; j < fl; j++){
22807                 f = fi[j];
22808             var v = this.ef[j](n);
22809             if (!f.convert) {
22810                 Roo.log('missing convert for ' + f.name);
22811                 Roo.log(f);
22812                 continue;
22813             }
22814             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22815             }
22816             var record = new Record(values, id);
22817             record.json = n;
22818             records[i] = record;
22819         }
22820         return {
22821             raw : o,
22822             success : success,
22823             records : records,
22824             totalRecords : totalRecords
22825         };
22826     }
22827 });/*
22828  * Based on:
22829  * Ext JS Library 1.1.1
22830  * Copyright(c) 2006-2007, Ext JS, LLC.
22831  *
22832  * Originally Released Under LGPL - original licence link has changed is not relivant.
22833  *
22834  * Fork - LGPL
22835  * <script type="text/javascript">
22836  */
22837
22838 /**
22839  * @class Roo.data.XmlReader
22840  * @extends Roo.data.DataReader
22841  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22842  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22843  * <p>
22844  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22845  * header in the HTTP response must be set to "text/xml".</em>
22846  * <p>
22847  * Example code:
22848  * <pre><code>
22849 var RecordDef = Roo.data.Record.create([
22850    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22851    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22852 ]);
22853 var myReader = new Roo.data.XmlReader({
22854    totalRecords: "results", // The element which contains the total dataset size (optional)
22855    record: "row",           // The repeated element which contains row information
22856    id: "id"                 // The element within the row that provides an ID for the record (optional)
22857 }, RecordDef);
22858 </code></pre>
22859  * <p>
22860  * This would consume an XML file like this:
22861  * <pre><code>
22862 &lt;?xml?>
22863 &lt;dataset>
22864  &lt;results>2&lt;/results>
22865  &lt;row>
22866    &lt;id>1&lt;/id>
22867    &lt;name>Bill&lt;/name>
22868    &lt;occupation>Gardener&lt;/occupation>
22869  &lt;/row>
22870  &lt;row>
22871    &lt;id>2&lt;/id>
22872    &lt;name>Ben&lt;/name>
22873    &lt;occupation>Horticulturalist&lt;/occupation>
22874  &lt;/row>
22875 &lt;/dataset>
22876 </code></pre>
22877  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22878  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22879  * paged from the remote server.
22880  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22881  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22882  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22883  * a record identifier value.
22884  * @constructor
22885  * Create a new XmlReader
22886  * @param {Object} meta Metadata configuration options
22887  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22888  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22889  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22890  */
22891 Roo.data.XmlReader = function(meta, recordType){
22892     meta = meta || {};
22893     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22894 };
22895 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22896     /**
22897      * This method is only used by a DataProxy which has retrieved data from a remote server.
22898          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22899          * to contain a method called 'responseXML' that returns an XML document object.
22900      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22901      * a cache of Roo.data.Records.
22902      */
22903     read : function(response){
22904         var doc = response.responseXML;
22905         if(!doc) {
22906             throw {message: "XmlReader.read: XML Document not available"};
22907         }
22908         return this.readRecords(doc);
22909     },
22910
22911     /**
22912      * Create a data block containing Roo.data.Records from an XML document.
22913          * @param {Object} doc A parsed XML document.
22914      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22915      * a cache of Roo.data.Records.
22916      */
22917     readRecords : function(doc){
22918         /**
22919          * After any data loads/reads, the raw XML Document is available for further custom processing.
22920          * @type XMLDocument
22921          */
22922         this.xmlData = doc;
22923         var root = doc.documentElement || doc;
22924         var q = Roo.DomQuery;
22925         var recordType = this.recordType, fields = recordType.prototype.fields;
22926         var sid = this.meta.id;
22927         var totalRecords = 0, success = true;
22928         if(this.meta.totalRecords){
22929             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22930         }
22931         
22932         if(this.meta.success){
22933             var sv = q.selectValue(this.meta.success, root, true);
22934             success = sv !== false && sv !== 'false';
22935         }
22936         var records = [];
22937         var ns = q.select(this.meta.record, root);
22938         for(var i = 0, len = ns.length; i < len; i++) {
22939                 var n = ns[i];
22940                 var values = {};
22941                 var id = sid ? q.selectValue(sid, n) : undefined;
22942                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22943                     var f = fields.items[j];
22944                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22945                     v = f.convert(v);
22946                     values[f.name] = v;
22947                 }
22948                 var record = new recordType(values, id);
22949                 record.node = n;
22950                 records[records.length] = record;
22951             }
22952
22953             return {
22954                 success : success,
22955                 records : records,
22956                 totalRecords : totalRecords || records.length
22957             };
22958     }
22959 });/*
22960  * Based on:
22961  * Ext JS Library 1.1.1
22962  * Copyright(c) 2006-2007, Ext JS, LLC.
22963  *
22964  * Originally Released Under LGPL - original licence link has changed is not relivant.
22965  *
22966  * Fork - LGPL
22967  * <script type="text/javascript">
22968  */
22969
22970 /**
22971  * @class Roo.data.ArrayReader
22972  * @extends Roo.data.DataReader
22973  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22974  * Each element of that Array represents a row of data fields. The
22975  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22976  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22977  * <p>
22978  * Example code:.
22979  * <pre><code>
22980 var RecordDef = Roo.data.Record.create([
22981     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22982     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22983 ]);
22984 var myReader = new Roo.data.ArrayReader({
22985     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22986 }, RecordDef);
22987 </code></pre>
22988  * <p>
22989  * This would consume an Array like this:
22990  * <pre><code>
22991 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22992   </code></pre>
22993  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22994  * @constructor
22995  * Create a new JsonReader
22996  * @param {Object} meta Metadata configuration options.
22997  * @param {Object} recordType Either an Array of field definition objects
22998  * as specified to {@link Roo.data.Record#create},
22999  * or an {@link Roo.data.Record} object
23000  * created using {@link Roo.data.Record#create}.
23001  */
23002 Roo.data.ArrayReader = function(meta, recordType){
23003     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
23004 };
23005
23006 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
23007     /**
23008      * Create a data block containing Roo.data.Records from an XML document.
23009      * @param {Object} o An Array of row objects which represents the dataset.
23010      * @return {Object} data A data block which is used by an Roo.data.Store object as
23011      * a cache of Roo.data.Records.
23012      */
23013     readRecords : function(o){
23014         var sid = this.meta ? this.meta.id : null;
23015         var recordType = this.recordType, fields = recordType.prototype.fields;
23016         var records = [];
23017         var root = o;
23018             for(var i = 0; i < root.length; i++){
23019                     var n = root[i];
23020                 var values = {};
23021                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23022                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23023                 var f = fields.items[j];
23024                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23025                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23026                 v = f.convert(v);
23027                 values[f.name] = v;
23028             }
23029                 var record = new recordType(values, id);
23030                 record.json = n;
23031                 records[records.length] = record;
23032             }
23033             return {
23034                 records : records,
23035                 totalRecords : records.length
23036             };
23037     }
23038 });/*
23039  * Based on:
23040  * Ext JS Library 1.1.1
23041  * Copyright(c) 2006-2007, Ext JS, LLC.
23042  *
23043  * Originally Released Under LGPL - original licence link has changed is not relivant.
23044  *
23045  * Fork - LGPL
23046  * <script type="text/javascript">
23047  */
23048
23049
23050 /**
23051  * @class Roo.data.Tree
23052  * @extends Roo.util.Observable
23053  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23054  * in the tree have most standard DOM functionality.
23055  * @constructor
23056  * @param {Node} root (optional) The root node
23057  */
23058 Roo.data.Tree = function(root){
23059    this.nodeHash = {};
23060    /**
23061     * The root node for this tree
23062     * @type Node
23063     */
23064    this.root = null;
23065    if(root){
23066        this.setRootNode(root);
23067    }
23068    this.addEvents({
23069        /**
23070         * @event append
23071         * Fires when a new child node is appended to a node in this tree.
23072         * @param {Tree} tree The owner tree
23073         * @param {Node} parent The parent node
23074         * @param {Node} node The newly appended node
23075         * @param {Number} index The index of the newly appended node
23076         */
23077        "append" : true,
23078        /**
23079         * @event remove
23080         * Fires when a child node is removed from a node in this tree.
23081         * @param {Tree} tree The owner tree
23082         * @param {Node} parent The parent node
23083         * @param {Node} node The child node removed
23084         */
23085        "remove" : true,
23086        /**
23087         * @event move
23088         * Fires when a node is moved to a new location in the tree
23089         * @param {Tree} tree The owner tree
23090         * @param {Node} node The node moved
23091         * @param {Node} oldParent The old parent of this node
23092         * @param {Node} newParent The new parent of this node
23093         * @param {Number} index The index it was moved to
23094         */
23095        "move" : true,
23096        /**
23097         * @event insert
23098         * Fires when a new child node is inserted in a node in this tree.
23099         * @param {Tree} tree The owner tree
23100         * @param {Node} parent The parent node
23101         * @param {Node} node The child node inserted
23102         * @param {Node} refNode The child node the node was inserted before
23103         */
23104        "insert" : true,
23105        /**
23106         * @event beforeappend
23107         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23108         * @param {Tree} tree The owner tree
23109         * @param {Node} parent The parent node
23110         * @param {Node} node The child node to be appended
23111         */
23112        "beforeappend" : true,
23113        /**
23114         * @event beforeremove
23115         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23116         * @param {Tree} tree The owner tree
23117         * @param {Node} parent The parent node
23118         * @param {Node} node The child node to be removed
23119         */
23120        "beforeremove" : true,
23121        /**
23122         * @event beforemove
23123         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23124         * @param {Tree} tree The owner tree
23125         * @param {Node} node The node being moved
23126         * @param {Node} oldParent The parent of the node
23127         * @param {Node} newParent The new parent the node is moving to
23128         * @param {Number} index The index it is being moved to
23129         */
23130        "beforemove" : true,
23131        /**
23132         * @event beforeinsert
23133         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23134         * @param {Tree} tree The owner tree
23135         * @param {Node} parent The parent node
23136         * @param {Node} node The child node to be inserted
23137         * @param {Node} refNode The child node the node is being inserted before
23138         */
23139        "beforeinsert" : true
23140    });
23141
23142     Roo.data.Tree.superclass.constructor.call(this);
23143 };
23144
23145 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23146     pathSeparator: "/",
23147
23148     proxyNodeEvent : function(){
23149         return this.fireEvent.apply(this, arguments);
23150     },
23151
23152     /**
23153      * Returns the root node for this tree.
23154      * @return {Node}
23155      */
23156     getRootNode : function(){
23157         return this.root;
23158     },
23159
23160     /**
23161      * Sets the root node for this tree.
23162      * @param {Node} node
23163      * @return {Node}
23164      */
23165     setRootNode : function(node){
23166         this.root = node;
23167         node.ownerTree = this;
23168         node.isRoot = true;
23169         this.registerNode(node);
23170         return node;
23171     },
23172
23173     /**
23174      * Gets a node in this tree by its id.
23175      * @param {String} id
23176      * @return {Node}
23177      */
23178     getNodeById : function(id){
23179         return this.nodeHash[id];
23180     },
23181
23182     registerNode : function(node){
23183         this.nodeHash[node.id] = node;
23184     },
23185
23186     unregisterNode : function(node){
23187         delete this.nodeHash[node.id];
23188     },
23189
23190     toString : function(){
23191         return "[Tree"+(this.id?" "+this.id:"")+"]";
23192     }
23193 });
23194
23195 /**
23196  * @class Roo.data.Node
23197  * @extends Roo.util.Observable
23198  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23199  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23200  * @constructor
23201  * @param {Object} attributes The attributes/config for the node
23202  */
23203 Roo.data.Node = function(attributes){
23204     /**
23205      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23206      * @type {Object}
23207      */
23208     this.attributes = attributes || {};
23209     this.leaf = this.attributes.leaf;
23210     /**
23211      * The node id. @type String
23212      */
23213     this.id = this.attributes.id;
23214     if(!this.id){
23215         this.id = Roo.id(null, "ynode-");
23216         this.attributes.id = this.id;
23217     }
23218      
23219     
23220     /**
23221      * All child nodes of this node. @type Array
23222      */
23223     this.childNodes = [];
23224     if(!this.childNodes.indexOf){ // indexOf is a must
23225         this.childNodes.indexOf = function(o){
23226             for(var i = 0, len = this.length; i < len; i++){
23227                 if(this[i] == o) {
23228                     return i;
23229                 }
23230             }
23231             return -1;
23232         };
23233     }
23234     /**
23235      * The parent node for this node. @type Node
23236      */
23237     this.parentNode = null;
23238     /**
23239      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23240      */
23241     this.firstChild = null;
23242     /**
23243      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23244      */
23245     this.lastChild = null;
23246     /**
23247      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23248      */
23249     this.previousSibling = null;
23250     /**
23251      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23252      */
23253     this.nextSibling = null;
23254
23255     this.addEvents({
23256        /**
23257         * @event append
23258         * Fires when a new child node is appended
23259         * @param {Tree} tree The owner tree
23260         * @param {Node} this This node
23261         * @param {Node} node The newly appended node
23262         * @param {Number} index The index of the newly appended node
23263         */
23264        "append" : true,
23265        /**
23266         * @event remove
23267         * Fires when a child node is removed
23268         * @param {Tree} tree The owner tree
23269         * @param {Node} this This node
23270         * @param {Node} node The removed node
23271         */
23272        "remove" : true,
23273        /**
23274         * @event move
23275         * Fires when this node is moved to a new location in the tree
23276         * @param {Tree} tree The owner tree
23277         * @param {Node} this This node
23278         * @param {Node} oldParent The old parent of this node
23279         * @param {Node} newParent The new parent of this node
23280         * @param {Number} index The index it was moved to
23281         */
23282        "move" : true,
23283        /**
23284         * @event insert
23285         * Fires when a new child node is inserted.
23286         * @param {Tree} tree The owner tree
23287         * @param {Node} this This node
23288         * @param {Node} node The child node inserted
23289         * @param {Node} refNode The child node the node was inserted before
23290         */
23291        "insert" : true,
23292        /**
23293         * @event beforeappend
23294         * Fires before a new child is appended, return false to cancel the append.
23295         * @param {Tree} tree The owner tree
23296         * @param {Node} this This node
23297         * @param {Node} node The child node to be appended
23298         */
23299        "beforeappend" : true,
23300        /**
23301         * @event beforeremove
23302         * Fires before a child is removed, return false to cancel the remove.
23303         * @param {Tree} tree The owner tree
23304         * @param {Node} this This node
23305         * @param {Node} node The child node to be removed
23306         */
23307        "beforeremove" : true,
23308        /**
23309         * @event beforemove
23310         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23311         * @param {Tree} tree The owner tree
23312         * @param {Node} this This node
23313         * @param {Node} oldParent The parent of this node
23314         * @param {Node} newParent The new parent this node is moving to
23315         * @param {Number} index The index it is being moved to
23316         */
23317        "beforemove" : true,
23318        /**
23319         * @event beforeinsert
23320         * Fires before a new child is inserted, return false to cancel the insert.
23321         * @param {Tree} tree The owner tree
23322         * @param {Node} this This node
23323         * @param {Node} node The child node to be inserted
23324         * @param {Node} refNode The child node the node is being inserted before
23325         */
23326        "beforeinsert" : true
23327    });
23328     this.listeners = this.attributes.listeners;
23329     Roo.data.Node.superclass.constructor.call(this);
23330 };
23331
23332 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23333     fireEvent : function(evtName){
23334         // first do standard event for this node
23335         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23336             return false;
23337         }
23338         // then bubble it up to the tree if the event wasn't cancelled
23339         var ot = this.getOwnerTree();
23340         if(ot){
23341             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23342                 return false;
23343             }
23344         }
23345         return true;
23346     },
23347
23348     /**
23349      * Returns true if this node is a leaf
23350      * @return {Boolean}
23351      */
23352     isLeaf : function(){
23353         return this.leaf === true;
23354     },
23355
23356     // private
23357     setFirstChild : function(node){
23358         this.firstChild = node;
23359     },
23360
23361     //private
23362     setLastChild : function(node){
23363         this.lastChild = node;
23364     },
23365
23366
23367     /**
23368      * Returns true if this node is the last child of its parent
23369      * @return {Boolean}
23370      */
23371     isLast : function(){
23372        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23373     },
23374
23375     /**
23376      * Returns true if this node is the first child of its parent
23377      * @return {Boolean}
23378      */
23379     isFirst : function(){
23380        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23381     },
23382
23383     hasChildNodes : function(){
23384         return !this.isLeaf() && this.childNodes.length > 0;
23385     },
23386
23387     /**
23388      * Insert node(s) as the last child node of this node.
23389      * @param {Node/Array} node The node or Array of nodes to append
23390      * @return {Node} The appended node if single append, or null if an array was passed
23391      */
23392     appendChild : function(node){
23393         var multi = false;
23394         if(node instanceof Array){
23395             multi = node;
23396         }else if(arguments.length > 1){
23397             multi = arguments;
23398         }
23399         // if passed an array or multiple args do them one by one
23400         if(multi){
23401             for(var i = 0, len = multi.length; i < len; i++) {
23402                 this.appendChild(multi[i]);
23403             }
23404         }else{
23405             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23406                 return false;
23407             }
23408             var index = this.childNodes.length;
23409             var oldParent = node.parentNode;
23410             // it's a move, make sure we move it cleanly
23411             if(oldParent){
23412                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23413                     return false;
23414                 }
23415                 oldParent.removeChild(node);
23416             }
23417             index = this.childNodes.length;
23418             if(index == 0){
23419                 this.setFirstChild(node);
23420             }
23421             this.childNodes.push(node);
23422             node.parentNode = this;
23423             var ps = this.childNodes[index-1];
23424             if(ps){
23425                 node.previousSibling = ps;
23426                 ps.nextSibling = node;
23427             }else{
23428                 node.previousSibling = null;
23429             }
23430             node.nextSibling = null;
23431             this.setLastChild(node);
23432             node.setOwnerTree(this.getOwnerTree());
23433             this.fireEvent("append", this.ownerTree, this, node, index);
23434             if(oldParent){
23435                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23436             }
23437             return node;
23438         }
23439     },
23440
23441     /**
23442      * Removes a child node from this node.
23443      * @param {Node} node The node to remove
23444      * @return {Node} The removed node
23445      */
23446     removeChild : function(node){
23447         var index = this.childNodes.indexOf(node);
23448         if(index == -1){
23449             return false;
23450         }
23451         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23452             return false;
23453         }
23454
23455         // remove it from childNodes collection
23456         this.childNodes.splice(index, 1);
23457
23458         // update siblings
23459         if(node.previousSibling){
23460             node.previousSibling.nextSibling = node.nextSibling;
23461         }
23462         if(node.nextSibling){
23463             node.nextSibling.previousSibling = node.previousSibling;
23464         }
23465
23466         // update child refs
23467         if(this.firstChild == node){
23468             this.setFirstChild(node.nextSibling);
23469         }
23470         if(this.lastChild == node){
23471             this.setLastChild(node.previousSibling);
23472         }
23473
23474         node.setOwnerTree(null);
23475         // clear any references from the node
23476         node.parentNode = null;
23477         node.previousSibling = null;
23478         node.nextSibling = null;
23479         this.fireEvent("remove", this.ownerTree, this, node);
23480         return node;
23481     },
23482
23483     /**
23484      * Inserts the first node before the second node in this nodes childNodes collection.
23485      * @param {Node} node The node to insert
23486      * @param {Node} refNode The node to insert before (if null the node is appended)
23487      * @return {Node} The inserted node
23488      */
23489     insertBefore : function(node, refNode){
23490         if(!refNode){ // like standard Dom, refNode can be null for append
23491             return this.appendChild(node);
23492         }
23493         // nothing to do
23494         if(node == refNode){
23495             return false;
23496         }
23497
23498         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23499             return false;
23500         }
23501         var index = this.childNodes.indexOf(refNode);
23502         var oldParent = node.parentNode;
23503         var refIndex = index;
23504
23505         // when moving internally, indexes will change after remove
23506         if(oldParent == this && this.childNodes.indexOf(node) < index){
23507             refIndex--;
23508         }
23509
23510         // it's a move, make sure we move it cleanly
23511         if(oldParent){
23512             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23513                 return false;
23514             }
23515             oldParent.removeChild(node);
23516         }
23517         if(refIndex == 0){
23518             this.setFirstChild(node);
23519         }
23520         this.childNodes.splice(refIndex, 0, node);
23521         node.parentNode = this;
23522         var ps = this.childNodes[refIndex-1];
23523         if(ps){
23524             node.previousSibling = ps;
23525             ps.nextSibling = node;
23526         }else{
23527             node.previousSibling = null;
23528         }
23529         node.nextSibling = refNode;
23530         refNode.previousSibling = node;
23531         node.setOwnerTree(this.getOwnerTree());
23532         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23533         if(oldParent){
23534             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23535         }
23536         return node;
23537     },
23538
23539     /**
23540      * Returns the child node at the specified index.
23541      * @param {Number} index
23542      * @return {Node}
23543      */
23544     item : function(index){
23545         return this.childNodes[index];
23546     },
23547
23548     /**
23549      * Replaces one child node in this node with another.
23550      * @param {Node} newChild The replacement node
23551      * @param {Node} oldChild The node to replace
23552      * @return {Node} The replaced node
23553      */
23554     replaceChild : function(newChild, oldChild){
23555         this.insertBefore(newChild, oldChild);
23556         this.removeChild(oldChild);
23557         return oldChild;
23558     },
23559
23560     /**
23561      * Returns the index of a child node
23562      * @param {Node} node
23563      * @return {Number} The index of the node or -1 if it was not found
23564      */
23565     indexOf : function(child){
23566         return this.childNodes.indexOf(child);
23567     },
23568
23569     /**
23570      * Returns the tree this node is in.
23571      * @return {Tree}
23572      */
23573     getOwnerTree : function(){
23574         // if it doesn't have one, look for one
23575         if(!this.ownerTree){
23576             var p = this;
23577             while(p){
23578                 if(p.ownerTree){
23579                     this.ownerTree = p.ownerTree;
23580                     break;
23581                 }
23582                 p = p.parentNode;
23583             }
23584         }
23585         return this.ownerTree;
23586     },
23587
23588     /**
23589      * Returns depth of this node (the root node has a depth of 0)
23590      * @return {Number}
23591      */
23592     getDepth : function(){
23593         var depth = 0;
23594         var p = this;
23595         while(p.parentNode){
23596             ++depth;
23597             p = p.parentNode;
23598         }
23599         return depth;
23600     },
23601
23602     // private
23603     setOwnerTree : function(tree){
23604         // if it's move, we need to update everyone
23605         if(tree != this.ownerTree){
23606             if(this.ownerTree){
23607                 this.ownerTree.unregisterNode(this);
23608             }
23609             this.ownerTree = tree;
23610             var cs = this.childNodes;
23611             for(var i = 0, len = cs.length; i < len; i++) {
23612                 cs[i].setOwnerTree(tree);
23613             }
23614             if(tree){
23615                 tree.registerNode(this);
23616             }
23617         }
23618     },
23619
23620     /**
23621      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23622      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23623      * @return {String} The path
23624      */
23625     getPath : function(attr){
23626         attr = attr || "id";
23627         var p = this.parentNode;
23628         var b = [this.attributes[attr]];
23629         while(p){
23630             b.unshift(p.attributes[attr]);
23631             p = p.parentNode;
23632         }
23633         var sep = this.getOwnerTree().pathSeparator;
23634         return sep + b.join(sep);
23635     },
23636
23637     /**
23638      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23639      * function call will be the scope provided or the current node. The arguments to the function
23640      * will be the args provided or the current node. If the function returns false at any point,
23641      * the bubble is stopped.
23642      * @param {Function} fn The function to call
23643      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23644      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23645      */
23646     bubble : function(fn, scope, args){
23647         var p = this;
23648         while(p){
23649             if(fn.call(scope || p, args || p) === false){
23650                 break;
23651             }
23652             p = p.parentNode;
23653         }
23654     },
23655
23656     /**
23657      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23658      * function call will be the scope provided or the current node. The arguments to the function
23659      * will be the args provided or the current node. If the function returns false at any point,
23660      * the cascade is stopped on that branch.
23661      * @param {Function} fn The function to call
23662      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23663      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23664      */
23665     cascade : function(fn, scope, args){
23666         if(fn.call(scope || this, args || this) !== false){
23667             var cs = this.childNodes;
23668             for(var i = 0, len = cs.length; i < len; i++) {
23669                 cs[i].cascade(fn, scope, args);
23670             }
23671         }
23672     },
23673
23674     /**
23675      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23676      * function call will be the scope provided or the current node. The arguments to the function
23677      * will be the args provided or the current node. If the function returns false at any point,
23678      * the iteration stops.
23679      * @param {Function} fn The function to call
23680      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23681      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23682      */
23683     eachChild : function(fn, scope, args){
23684         var cs = this.childNodes;
23685         for(var i = 0, len = cs.length; i < len; i++) {
23686                 if(fn.call(scope || this, args || cs[i]) === false){
23687                     break;
23688                 }
23689         }
23690     },
23691
23692     /**
23693      * Finds the first child that has the attribute with the specified value.
23694      * @param {String} attribute The attribute name
23695      * @param {Mixed} value The value to search for
23696      * @return {Node} The found child or null if none was found
23697      */
23698     findChild : function(attribute, value){
23699         var cs = this.childNodes;
23700         for(var i = 0, len = cs.length; i < len; i++) {
23701                 if(cs[i].attributes[attribute] == value){
23702                     return cs[i];
23703                 }
23704         }
23705         return null;
23706     },
23707
23708     /**
23709      * Finds the first child by a custom function. The child matches if the function passed
23710      * returns true.
23711      * @param {Function} fn
23712      * @param {Object} scope (optional)
23713      * @return {Node} The found child or null if none was found
23714      */
23715     findChildBy : function(fn, scope){
23716         var cs = this.childNodes;
23717         for(var i = 0, len = cs.length; i < len; i++) {
23718                 if(fn.call(scope||cs[i], cs[i]) === true){
23719                     return cs[i];
23720                 }
23721         }
23722         return null;
23723     },
23724
23725     /**
23726      * Sorts this nodes children using the supplied sort function
23727      * @param {Function} fn
23728      * @param {Object} scope (optional)
23729      */
23730     sort : function(fn, scope){
23731         var cs = this.childNodes;
23732         var len = cs.length;
23733         if(len > 0){
23734             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23735             cs.sort(sortFn);
23736             for(var i = 0; i < len; i++){
23737                 var n = cs[i];
23738                 n.previousSibling = cs[i-1];
23739                 n.nextSibling = cs[i+1];
23740                 if(i == 0){
23741                     this.setFirstChild(n);
23742                 }
23743                 if(i == len-1){
23744                     this.setLastChild(n);
23745                 }
23746             }
23747         }
23748     },
23749
23750     /**
23751      * Returns true if this node is an ancestor (at any point) of the passed node.
23752      * @param {Node} node
23753      * @return {Boolean}
23754      */
23755     contains : function(node){
23756         return node.isAncestor(this);
23757     },
23758
23759     /**
23760      * Returns true if the passed node is an ancestor (at any point) of this node.
23761      * @param {Node} node
23762      * @return {Boolean}
23763      */
23764     isAncestor : function(node){
23765         var p = this.parentNode;
23766         while(p){
23767             if(p == node){
23768                 return true;
23769             }
23770             p = p.parentNode;
23771         }
23772         return false;
23773     },
23774
23775     toString : function(){
23776         return "[Node"+(this.id?" "+this.id:"")+"]";
23777     }
23778 });/*
23779  * Based on:
23780  * Ext JS Library 1.1.1
23781  * Copyright(c) 2006-2007, Ext JS, LLC.
23782  *
23783  * Originally Released Under LGPL - original licence link has changed is not relivant.
23784  *
23785  * Fork - LGPL
23786  * <script type="text/javascript">
23787  */
23788  (function(){ 
23789 /**
23790  * @class Roo.Layer
23791  * @extends Roo.Element
23792  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23793  * automatic maintaining of shadow/shim positions.
23794  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23795  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23796  * you can pass a string with a CSS class name. False turns off the shadow.
23797  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23798  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23799  * @cfg {String} cls CSS class to add to the element
23800  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23801  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23802  * @constructor
23803  * @param {Object} config An object with config options.
23804  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23805  */
23806
23807 Roo.Layer = function(config, existingEl){
23808     config = config || {};
23809     var dh = Roo.DomHelper;
23810     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23811     if(existingEl){
23812         this.dom = Roo.getDom(existingEl);
23813     }
23814     if(!this.dom){
23815         var o = config.dh || {tag: "div", cls: "x-layer"};
23816         this.dom = dh.append(pel, o);
23817     }
23818     if(config.cls){
23819         this.addClass(config.cls);
23820     }
23821     this.constrain = config.constrain !== false;
23822     this.visibilityMode = Roo.Element.VISIBILITY;
23823     if(config.id){
23824         this.id = this.dom.id = config.id;
23825     }else{
23826         this.id = Roo.id(this.dom);
23827     }
23828     this.zindex = config.zindex || this.getZIndex();
23829     this.position("absolute", this.zindex);
23830     if(config.shadow){
23831         this.shadowOffset = config.shadowOffset || 4;
23832         this.shadow = new Roo.Shadow({
23833             offset : this.shadowOffset,
23834             mode : config.shadow
23835         });
23836     }else{
23837         this.shadowOffset = 0;
23838     }
23839     this.useShim = config.shim !== false && Roo.useShims;
23840     this.useDisplay = config.useDisplay;
23841     this.hide();
23842 };
23843
23844 var supr = Roo.Element.prototype;
23845
23846 // shims are shared among layer to keep from having 100 iframes
23847 var shims = [];
23848
23849 Roo.extend(Roo.Layer, Roo.Element, {
23850
23851     getZIndex : function(){
23852         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23853     },
23854
23855     getShim : function(){
23856         if(!this.useShim){
23857             return null;
23858         }
23859         if(this.shim){
23860             return this.shim;
23861         }
23862         var shim = shims.shift();
23863         if(!shim){
23864             shim = this.createShim();
23865             shim.enableDisplayMode('block');
23866             shim.dom.style.display = 'none';
23867             shim.dom.style.visibility = 'visible';
23868         }
23869         var pn = this.dom.parentNode;
23870         if(shim.dom.parentNode != pn){
23871             pn.insertBefore(shim.dom, this.dom);
23872         }
23873         shim.setStyle('z-index', this.getZIndex()-2);
23874         this.shim = shim;
23875         return shim;
23876     },
23877
23878     hideShim : function(){
23879         if(this.shim){
23880             this.shim.setDisplayed(false);
23881             shims.push(this.shim);
23882             delete this.shim;
23883         }
23884     },
23885
23886     disableShadow : function(){
23887         if(this.shadow){
23888             this.shadowDisabled = true;
23889             this.shadow.hide();
23890             this.lastShadowOffset = this.shadowOffset;
23891             this.shadowOffset = 0;
23892         }
23893     },
23894
23895     enableShadow : function(show){
23896         if(this.shadow){
23897             this.shadowDisabled = false;
23898             this.shadowOffset = this.lastShadowOffset;
23899             delete this.lastShadowOffset;
23900             if(show){
23901                 this.sync(true);
23902             }
23903         }
23904     },
23905
23906     // private
23907     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23908     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23909     sync : function(doShow){
23910         var sw = this.shadow;
23911         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23912             var sh = this.getShim();
23913
23914             var w = this.getWidth(),
23915                 h = this.getHeight();
23916
23917             var l = this.getLeft(true),
23918                 t = this.getTop(true);
23919
23920             if(sw && !this.shadowDisabled){
23921                 if(doShow && !sw.isVisible()){
23922                     sw.show(this);
23923                 }else{
23924                     sw.realign(l, t, w, h);
23925                 }
23926                 if(sh){
23927                     if(doShow){
23928                        sh.show();
23929                     }
23930                     // fit the shim behind the shadow, so it is shimmed too
23931                     var a = sw.adjusts, s = sh.dom.style;
23932                     s.left = (Math.min(l, l+a.l))+"px";
23933                     s.top = (Math.min(t, t+a.t))+"px";
23934                     s.width = (w+a.w)+"px";
23935                     s.height = (h+a.h)+"px";
23936                 }
23937             }else if(sh){
23938                 if(doShow){
23939                    sh.show();
23940                 }
23941                 sh.setSize(w, h);
23942                 sh.setLeftTop(l, t);
23943             }
23944             
23945         }
23946     },
23947
23948     // private
23949     destroy : function(){
23950         this.hideShim();
23951         if(this.shadow){
23952             this.shadow.hide();
23953         }
23954         this.removeAllListeners();
23955         var pn = this.dom.parentNode;
23956         if(pn){
23957             pn.removeChild(this.dom);
23958         }
23959         Roo.Element.uncache(this.id);
23960     },
23961
23962     remove : function(){
23963         this.destroy();
23964     },
23965
23966     // private
23967     beginUpdate : function(){
23968         this.updating = true;
23969     },
23970
23971     // private
23972     endUpdate : function(){
23973         this.updating = false;
23974         this.sync(true);
23975     },
23976
23977     // private
23978     hideUnders : function(negOffset){
23979         if(this.shadow){
23980             this.shadow.hide();
23981         }
23982         this.hideShim();
23983     },
23984
23985     // private
23986     constrainXY : function(){
23987         if(this.constrain){
23988             var vw = Roo.lib.Dom.getViewWidth(),
23989                 vh = Roo.lib.Dom.getViewHeight();
23990             var s = Roo.get(document).getScroll();
23991
23992             var xy = this.getXY();
23993             var x = xy[0], y = xy[1];   
23994             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23995             // only move it if it needs it
23996             var moved = false;
23997             // first validate right/bottom
23998             if((x + w) > vw+s.left){
23999                 x = vw - w - this.shadowOffset;
24000                 moved = true;
24001             }
24002             if((y + h) > vh+s.top){
24003                 y = vh - h - this.shadowOffset;
24004                 moved = true;
24005             }
24006             // then make sure top/left isn't negative
24007             if(x < s.left){
24008                 x = s.left;
24009                 moved = true;
24010             }
24011             if(y < s.top){
24012                 y = s.top;
24013                 moved = true;
24014             }
24015             if(moved){
24016                 if(this.avoidY){
24017                     var ay = this.avoidY;
24018                     if(y <= ay && (y+h) >= ay){
24019                         y = ay-h-5;   
24020                     }
24021                 }
24022                 xy = [x, y];
24023                 this.storeXY(xy);
24024                 supr.setXY.call(this, xy);
24025                 this.sync();
24026             }
24027         }
24028     },
24029
24030     isVisible : function(){
24031         return this.visible;    
24032     },
24033
24034     // private
24035     showAction : function(){
24036         this.visible = true; // track visibility to prevent getStyle calls
24037         if(this.useDisplay === true){
24038             this.setDisplayed("");
24039         }else if(this.lastXY){
24040             supr.setXY.call(this, this.lastXY);
24041         }else if(this.lastLT){
24042             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24043         }
24044     },
24045
24046     // private
24047     hideAction : function(){
24048         this.visible = false;
24049         if(this.useDisplay === true){
24050             this.setDisplayed(false);
24051         }else{
24052             this.setLeftTop(-10000,-10000);
24053         }
24054     },
24055
24056     // overridden Element method
24057     setVisible : function(v, a, d, c, e){
24058         if(v){
24059             this.showAction();
24060         }
24061         if(a && v){
24062             var cb = function(){
24063                 this.sync(true);
24064                 if(c){
24065                     c();
24066                 }
24067             }.createDelegate(this);
24068             supr.setVisible.call(this, true, true, d, cb, e);
24069         }else{
24070             if(!v){
24071                 this.hideUnders(true);
24072             }
24073             var cb = c;
24074             if(a){
24075                 cb = function(){
24076                     this.hideAction();
24077                     if(c){
24078                         c();
24079                     }
24080                 }.createDelegate(this);
24081             }
24082             supr.setVisible.call(this, v, a, d, cb, e);
24083             if(v){
24084                 this.sync(true);
24085             }else if(!a){
24086                 this.hideAction();
24087             }
24088         }
24089     },
24090
24091     storeXY : function(xy){
24092         delete this.lastLT;
24093         this.lastXY = xy;
24094     },
24095
24096     storeLeftTop : function(left, top){
24097         delete this.lastXY;
24098         this.lastLT = [left, top];
24099     },
24100
24101     // private
24102     beforeFx : function(){
24103         this.beforeAction();
24104         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24105     },
24106
24107     // private
24108     afterFx : function(){
24109         Roo.Layer.superclass.afterFx.apply(this, arguments);
24110         this.sync(this.isVisible());
24111     },
24112
24113     // private
24114     beforeAction : function(){
24115         if(!this.updating && this.shadow){
24116             this.shadow.hide();
24117         }
24118     },
24119
24120     // overridden Element method
24121     setLeft : function(left){
24122         this.storeLeftTop(left, this.getTop(true));
24123         supr.setLeft.apply(this, arguments);
24124         this.sync();
24125     },
24126
24127     setTop : function(top){
24128         this.storeLeftTop(this.getLeft(true), top);
24129         supr.setTop.apply(this, arguments);
24130         this.sync();
24131     },
24132
24133     setLeftTop : function(left, top){
24134         this.storeLeftTop(left, top);
24135         supr.setLeftTop.apply(this, arguments);
24136         this.sync();
24137     },
24138
24139     setXY : function(xy, a, d, c, e){
24140         this.fixDisplay();
24141         this.beforeAction();
24142         this.storeXY(xy);
24143         var cb = this.createCB(c);
24144         supr.setXY.call(this, xy, a, d, cb, e);
24145         if(!a){
24146             cb();
24147         }
24148     },
24149
24150     // private
24151     createCB : function(c){
24152         var el = this;
24153         return function(){
24154             el.constrainXY();
24155             el.sync(true);
24156             if(c){
24157                 c();
24158             }
24159         };
24160     },
24161
24162     // overridden Element method
24163     setX : function(x, a, d, c, e){
24164         this.setXY([x, this.getY()], a, d, c, e);
24165     },
24166
24167     // overridden Element method
24168     setY : function(y, a, d, c, e){
24169         this.setXY([this.getX(), y], a, d, c, e);
24170     },
24171
24172     // overridden Element method
24173     setSize : function(w, h, a, d, c, e){
24174         this.beforeAction();
24175         var cb = this.createCB(c);
24176         supr.setSize.call(this, w, h, a, d, cb, e);
24177         if(!a){
24178             cb();
24179         }
24180     },
24181
24182     // overridden Element method
24183     setWidth : function(w, a, d, c, e){
24184         this.beforeAction();
24185         var cb = this.createCB(c);
24186         supr.setWidth.call(this, w, a, d, cb, e);
24187         if(!a){
24188             cb();
24189         }
24190     },
24191
24192     // overridden Element method
24193     setHeight : function(h, a, d, c, e){
24194         this.beforeAction();
24195         var cb = this.createCB(c);
24196         supr.setHeight.call(this, h, a, d, cb, e);
24197         if(!a){
24198             cb();
24199         }
24200     },
24201
24202     // overridden Element method
24203     setBounds : function(x, y, w, h, a, d, c, e){
24204         this.beforeAction();
24205         var cb = this.createCB(c);
24206         if(!a){
24207             this.storeXY([x, y]);
24208             supr.setXY.call(this, [x, y]);
24209             supr.setSize.call(this, w, h, a, d, cb, e);
24210             cb();
24211         }else{
24212             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24213         }
24214         return this;
24215     },
24216     
24217     /**
24218      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24219      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24220      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24221      * @param {Number} zindex The new z-index to set
24222      * @return {this} The Layer
24223      */
24224     setZIndex : function(zindex){
24225         this.zindex = zindex;
24226         this.setStyle("z-index", zindex + 2);
24227         if(this.shadow){
24228             this.shadow.setZIndex(zindex + 1);
24229         }
24230         if(this.shim){
24231             this.shim.setStyle("z-index", zindex);
24232         }
24233     }
24234 });
24235 })();/*
24236  * Based on:
24237  * Ext JS Library 1.1.1
24238  * Copyright(c) 2006-2007, Ext JS, LLC.
24239  *
24240  * Originally Released Under LGPL - original licence link has changed is not relivant.
24241  *
24242  * Fork - LGPL
24243  * <script type="text/javascript">
24244  */
24245
24246
24247 /**
24248  * @class Roo.Shadow
24249  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24250  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24251  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24252  * @constructor
24253  * Create a new Shadow
24254  * @param {Object} config The config object
24255  */
24256 Roo.Shadow = function(config){
24257     Roo.apply(this, config);
24258     if(typeof this.mode != "string"){
24259         this.mode = this.defaultMode;
24260     }
24261     var o = this.offset, a = {h: 0};
24262     var rad = Math.floor(this.offset/2);
24263     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24264         case "drop":
24265             a.w = 0;
24266             a.l = a.t = o;
24267             a.t -= 1;
24268             if(Roo.isIE){
24269                 a.l -= this.offset + rad;
24270                 a.t -= this.offset + rad;
24271                 a.w -= rad;
24272                 a.h -= rad;
24273                 a.t += 1;
24274             }
24275         break;
24276         case "sides":
24277             a.w = (o*2);
24278             a.l = -o;
24279             a.t = o-1;
24280             if(Roo.isIE){
24281                 a.l -= (this.offset - rad);
24282                 a.t -= this.offset + rad;
24283                 a.l += 1;
24284                 a.w -= (this.offset - rad)*2;
24285                 a.w -= rad + 1;
24286                 a.h -= 1;
24287             }
24288         break;
24289         case "frame":
24290             a.w = a.h = (o*2);
24291             a.l = a.t = -o;
24292             a.t += 1;
24293             a.h -= 2;
24294             if(Roo.isIE){
24295                 a.l -= (this.offset - rad);
24296                 a.t -= (this.offset - rad);
24297                 a.l += 1;
24298                 a.w -= (this.offset + rad + 1);
24299                 a.h -= (this.offset + rad);
24300                 a.h += 1;
24301             }
24302         break;
24303     };
24304
24305     this.adjusts = a;
24306 };
24307
24308 Roo.Shadow.prototype = {
24309     /**
24310      * @cfg {String} mode
24311      * The shadow display mode.  Supports the following options:<br />
24312      * sides: Shadow displays on both sides and bottom only<br />
24313      * frame: Shadow displays equally on all four sides<br />
24314      * drop: Traditional bottom-right drop shadow (default)
24315      */
24316     /**
24317      * @cfg {String} offset
24318      * The number of pixels to offset the shadow from the element (defaults to 4)
24319      */
24320     offset: 4,
24321
24322     // private
24323     defaultMode: "drop",
24324
24325     /**
24326      * Displays the shadow under the target element
24327      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24328      */
24329     show : function(target){
24330         target = Roo.get(target);
24331         if(!this.el){
24332             this.el = Roo.Shadow.Pool.pull();
24333             if(this.el.dom.nextSibling != target.dom){
24334                 this.el.insertBefore(target);
24335             }
24336         }
24337         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24338         if(Roo.isIE){
24339             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24340         }
24341         this.realign(
24342             target.getLeft(true),
24343             target.getTop(true),
24344             target.getWidth(),
24345             target.getHeight()
24346         );
24347         this.el.dom.style.display = "block";
24348     },
24349
24350     /**
24351      * Returns true if the shadow is visible, else false
24352      */
24353     isVisible : function(){
24354         return this.el ? true : false;  
24355     },
24356
24357     /**
24358      * Direct alignment when values are already available. Show must be called at least once before
24359      * calling this method to ensure it is initialized.
24360      * @param {Number} left The target element left position
24361      * @param {Number} top The target element top position
24362      * @param {Number} width The target element width
24363      * @param {Number} height The target element height
24364      */
24365     realign : function(l, t, w, h){
24366         if(!this.el){
24367             return;
24368         }
24369         var a = this.adjusts, d = this.el.dom, s = d.style;
24370         var iea = 0;
24371         s.left = (l+a.l)+"px";
24372         s.top = (t+a.t)+"px";
24373         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24374  
24375         if(s.width != sws || s.height != shs){
24376             s.width = sws;
24377             s.height = shs;
24378             if(!Roo.isIE){
24379                 var cn = d.childNodes;
24380                 var sww = Math.max(0, (sw-12))+"px";
24381                 cn[0].childNodes[1].style.width = sww;
24382                 cn[1].childNodes[1].style.width = sww;
24383                 cn[2].childNodes[1].style.width = sww;
24384                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24385             }
24386         }
24387     },
24388
24389     /**
24390      * Hides this shadow
24391      */
24392     hide : function(){
24393         if(this.el){
24394             this.el.dom.style.display = "none";
24395             Roo.Shadow.Pool.push(this.el);
24396             delete this.el;
24397         }
24398     },
24399
24400     /**
24401      * Adjust the z-index of this shadow
24402      * @param {Number} zindex The new z-index
24403      */
24404     setZIndex : function(z){
24405         this.zIndex = z;
24406         if(this.el){
24407             this.el.setStyle("z-index", z);
24408         }
24409     }
24410 };
24411
24412 // Private utility class that manages the internal Shadow cache
24413 Roo.Shadow.Pool = function(){
24414     var p = [];
24415     var markup = Roo.isIE ?
24416                  '<div class="x-ie-shadow"></div>' :
24417                  '<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>';
24418     return {
24419         pull : function(){
24420             var sh = p.shift();
24421             if(!sh){
24422                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24423                 sh.autoBoxAdjust = false;
24424             }
24425             return sh;
24426         },
24427
24428         push : function(sh){
24429             p.push(sh);
24430         }
24431     };
24432 }();/*
24433  * Based on:
24434  * Ext JS Library 1.1.1
24435  * Copyright(c) 2006-2007, Ext JS, LLC.
24436  *
24437  * Originally Released Under LGPL - original licence link has changed is not relivant.
24438  *
24439  * Fork - LGPL
24440  * <script type="text/javascript">
24441  */
24442
24443
24444 /**
24445  * @class Roo.SplitBar
24446  * @extends Roo.util.Observable
24447  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24448  * <br><br>
24449  * Usage:
24450  * <pre><code>
24451 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24452                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24453 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24454 split.minSize = 100;
24455 split.maxSize = 600;
24456 split.animate = true;
24457 split.on('moved', splitterMoved);
24458 </code></pre>
24459  * @constructor
24460  * Create a new SplitBar
24461  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24462  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24463  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24464  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24465                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24466                         position of the SplitBar).
24467  */
24468 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24469     
24470     /** @private */
24471     this.el = Roo.get(dragElement, true);
24472     this.el.dom.unselectable = "on";
24473     /** @private */
24474     this.resizingEl = Roo.get(resizingElement, true);
24475
24476     /**
24477      * @private
24478      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24479      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24480      * @type Number
24481      */
24482     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24483     
24484     /**
24485      * The minimum size of the resizing element. (Defaults to 0)
24486      * @type Number
24487      */
24488     this.minSize = 0;
24489     
24490     /**
24491      * The maximum size of the resizing element. (Defaults to 2000)
24492      * @type Number
24493      */
24494     this.maxSize = 2000;
24495     
24496     /**
24497      * Whether to animate the transition to the new size
24498      * @type Boolean
24499      */
24500     this.animate = false;
24501     
24502     /**
24503      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24504      * @type Boolean
24505      */
24506     this.useShim = false;
24507     
24508     /** @private */
24509     this.shim = null;
24510     
24511     if(!existingProxy){
24512         /** @private */
24513         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24514     }else{
24515         this.proxy = Roo.get(existingProxy).dom;
24516     }
24517     /** @private */
24518     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24519     
24520     /** @private */
24521     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24522     
24523     /** @private */
24524     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24525     
24526     /** @private */
24527     this.dragSpecs = {};
24528     
24529     /**
24530      * @private The adapter to use to positon and resize elements
24531      */
24532     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24533     this.adapter.init(this);
24534     
24535     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24536         /** @private */
24537         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24538         this.el.addClass("x-splitbar-h");
24539     }else{
24540         /** @private */
24541         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24542         this.el.addClass("x-splitbar-v");
24543     }
24544     
24545     this.addEvents({
24546         /**
24547          * @event resize
24548          * Fires when the splitter is moved (alias for {@link #event-moved})
24549          * @param {Roo.SplitBar} this
24550          * @param {Number} newSize the new width or height
24551          */
24552         "resize" : true,
24553         /**
24554          * @event moved
24555          * Fires when the splitter is moved
24556          * @param {Roo.SplitBar} this
24557          * @param {Number} newSize the new width or height
24558          */
24559         "moved" : true,
24560         /**
24561          * @event beforeresize
24562          * Fires before the splitter is dragged
24563          * @param {Roo.SplitBar} this
24564          */
24565         "beforeresize" : true,
24566
24567         "beforeapply" : true
24568     });
24569
24570     Roo.util.Observable.call(this);
24571 };
24572
24573 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24574     onStartProxyDrag : function(x, y){
24575         this.fireEvent("beforeresize", this);
24576         if(!this.overlay){
24577             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24578             o.unselectable();
24579             o.enableDisplayMode("block");
24580             // all splitbars share the same overlay
24581             Roo.SplitBar.prototype.overlay = o;
24582         }
24583         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24584         this.overlay.show();
24585         Roo.get(this.proxy).setDisplayed("block");
24586         var size = this.adapter.getElementSize(this);
24587         this.activeMinSize = this.getMinimumSize();;
24588         this.activeMaxSize = this.getMaximumSize();;
24589         var c1 = size - this.activeMinSize;
24590         var c2 = Math.max(this.activeMaxSize - size, 0);
24591         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24592             this.dd.resetConstraints();
24593             this.dd.setXConstraint(
24594                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24595                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24596             );
24597             this.dd.setYConstraint(0, 0);
24598         }else{
24599             this.dd.resetConstraints();
24600             this.dd.setXConstraint(0, 0);
24601             this.dd.setYConstraint(
24602                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24603                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24604             );
24605          }
24606         this.dragSpecs.startSize = size;
24607         this.dragSpecs.startPoint = [x, y];
24608         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24609     },
24610     
24611     /** 
24612      * @private Called after the drag operation by the DDProxy
24613      */
24614     onEndProxyDrag : function(e){
24615         Roo.get(this.proxy).setDisplayed(false);
24616         var endPoint = Roo.lib.Event.getXY(e);
24617         if(this.overlay){
24618             this.overlay.hide();
24619         }
24620         var newSize;
24621         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24622             newSize = this.dragSpecs.startSize + 
24623                 (this.placement == Roo.SplitBar.LEFT ?
24624                     endPoint[0] - this.dragSpecs.startPoint[0] :
24625                     this.dragSpecs.startPoint[0] - endPoint[0]
24626                 );
24627         }else{
24628             newSize = this.dragSpecs.startSize + 
24629                 (this.placement == Roo.SplitBar.TOP ?
24630                     endPoint[1] - this.dragSpecs.startPoint[1] :
24631                     this.dragSpecs.startPoint[1] - endPoint[1]
24632                 );
24633         }
24634         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24635         if(newSize != this.dragSpecs.startSize){
24636             if(this.fireEvent('beforeapply', this, newSize) !== false){
24637                 this.adapter.setElementSize(this, newSize);
24638                 this.fireEvent("moved", this, newSize);
24639                 this.fireEvent("resize", this, newSize);
24640             }
24641         }
24642     },
24643     
24644     /**
24645      * Get the adapter this SplitBar uses
24646      * @return The adapter object
24647      */
24648     getAdapter : function(){
24649         return this.adapter;
24650     },
24651     
24652     /**
24653      * Set the adapter this SplitBar uses
24654      * @param {Object} adapter A SplitBar adapter object
24655      */
24656     setAdapter : function(adapter){
24657         this.adapter = adapter;
24658         this.adapter.init(this);
24659     },
24660     
24661     /**
24662      * Gets the minimum size for the resizing element
24663      * @return {Number} The minimum size
24664      */
24665     getMinimumSize : function(){
24666         return this.minSize;
24667     },
24668     
24669     /**
24670      * Sets the minimum size for the resizing element
24671      * @param {Number} minSize The minimum size
24672      */
24673     setMinimumSize : function(minSize){
24674         this.minSize = minSize;
24675     },
24676     
24677     /**
24678      * Gets the maximum size for the resizing element
24679      * @return {Number} The maximum size
24680      */
24681     getMaximumSize : function(){
24682         return this.maxSize;
24683     },
24684     
24685     /**
24686      * Sets the maximum size for the resizing element
24687      * @param {Number} maxSize The maximum size
24688      */
24689     setMaximumSize : function(maxSize){
24690         this.maxSize = maxSize;
24691     },
24692     
24693     /**
24694      * Sets the initialize size for the resizing element
24695      * @param {Number} size The initial size
24696      */
24697     setCurrentSize : function(size){
24698         var oldAnimate = this.animate;
24699         this.animate = false;
24700         this.adapter.setElementSize(this, size);
24701         this.animate = oldAnimate;
24702     },
24703     
24704     /**
24705      * Destroy this splitbar. 
24706      * @param {Boolean} removeEl True to remove the element
24707      */
24708     destroy : function(removeEl){
24709         if(this.shim){
24710             this.shim.remove();
24711         }
24712         this.dd.unreg();
24713         this.proxy.parentNode.removeChild(this.proxy);
24714         if(removeEl){
24715             this.el.remove();
24716         }
24717     }
24718 });
24719
24720 /**
24721  * @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.
24722  */
24723 Roo.SplitBar.createProxy = function(dir){
24724     var proxy = new Roo.Element(document.createElement("div"));
24725     proxy.unselectable();
24726     var cls = 'x-splitbar-proxy';
24727     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24728     document.body.appendChild(proxy.dom);
24729     return proxy.dom;
24730 };
24731
24732 /** 
24733  * @class Roo.SplitBar.BasicLayoutAdapter
24734  * Default Adapter. It assumes the splitter and resizing element are not positioned
24735  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24736  */
24737 Roo.SplitBar.BasicLayoutAdapter = function(){
24738 };
24739
24740 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24741     // do nothing for now
24742     init : function(s){
24743     
24744     },
24745     /**
24746      * Called before drag operations to get the current size of the resizing element. 
24747      * @param {Roo.SplitBar} s The SplitBar using this adapter
24748      */
24749      getElementSize : function(s){
24750         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24751             return s.resizingEl.getWidth();
24752         }else{
24753             return s.resizingEl.getHeight();
24754         }
24755     },
24756     
24757     /**
24758      * Called after drag operations to set the size of the resizing element.
24759      * @param {Roo.SplitBar} s The SplitBar using this adapter
24760      * @param {Number} newSize The new size to set
24761      * @param {Function} onComplete A function to be invoked when resizing is complete
24762      */
24763     setElementSize : function(s, newSize, onComplete){
24764         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24765             if(!s.animate){
24766                 s.resizingEl.setWidth(newSize);
24767                 if(onComplete){
24768                     onComplete(s, newSize);
24769                 }
24770             }else{
24771                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24772             }
24773         }else{
24774             
24775             if(!s.animate){
24776                 s.resizingEl.setHeight(newSize);
24777                 if(onComplete){
24778                     onComplete(s, newSize);
24779                 }
24780             }else{
24781                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24782             }
24783         }
24784     }
24785 };
24786
24787 /** 
24788  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24789  * @extends Roo.SplitBar.BasicLayoutAdapter
24790  * Adapter that  moves the splitter element to align with the resized sizing element. 
24791  * Used with an absolute positioned SplitBar.
24792  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24793  * document.body, make sure you assign an id to the body element.
24794  */
24795 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24796     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24797     this.container = Roo.get(container);
24798 };
24799
24800 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24801     init : function(s){
24802         this.basic.init(s);
24803     },
24804     
24805     getElementSize : function(s){
24806         return this.basic.getElementSize(s);
24807     },
24808     
24809     setElementSize : function(s, newSize, onComplete){
24810         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24811     },
24812     
24813     moveSplitter : function(s){
24814         var yes = Roo.SplitBar;
24815         switch(s.placement){
24816             case yes.LEFT:
24817                 s.el.setX(s.resizingEl.getRight());
24818                 break;
24819             case yes.RIGHT:
24820                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24821                 break;
24822             case yes.TOP:
24823                 s.el.setY(s.resizingEl.getBottom());
24824                 break;
24825             case yes.BOTTOM:
24826                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24827                 break;
24828         }
24829     }
24830 };
24831
24832 /**
24833  * Orientation constant - Create a vertical SplitBar
24834  * @static
24835  * @type Number
24836  */
24837 Roo.SplitBar.VERTICAL = 1;
24838
24839 /**
24840  * Orientation constant - Create a horizontal SplitBar
24841  * @static
24842  * @type Number
24843  */
24844 Roo.SplitBar.HORIZONTAL = 2;
24845
24846 /**
24847  * Placement constant - The resizing element is to the left of the splitter element
24848  * @static
24849  * @type Number
24850  */
24851 Roo.SplitBar.LEFT = 1;
24852
24853 /**
24854  * Placement constant - The resizing element is to the right of the splitter element
24855  * @static
24856  * @type Number
24857  */
24858 Roo.SplitBar.RIGHT = 2;
24859
24860 /**
24861  * Placement constant - The resizing element is positioned above the splitter element
24862  * @static
24863  * @type Number
24864  */
24865 Roo.SplitBar.TOP = 3;
24866
24867 /**
24868  * Placement constant - The resizing element is positioned under splitter element
24869  * @static
24870  * @type Number
24871  */
24872 Roo.SplitBar.BOTTOM = 4;
24873 /*
24874  * Based on:
24875  * Ext JS Library 1.1.1
24876  * Copyright(c) 2006-2007, Ext JS, LLC.
24877  *
24878  * Originally Released Under LGPL - original licence link has changed is not relivant.
24879  *
24880  * Fork - LGPL
24881  * <script type="text/javascript">
24882  */
24883
24884 /**
24885  * @class Roo.View
24886  * @extends Roo.util.Observable
24887  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24888  * This class also supports single and multi selection modes. <br>
24889  * Create a data model bound view:
24890  <pre><code>
24891  var store = new Roo.data.Store(...);
24892
24893  var view = new Roo.View({
24894     el : "my-element",
24895     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24896  
24897     singleSelect: true,
24898     selectedClass: "ydataview-selected",
24899     store: store
24900  });
24901
24902  // listen for node click?
24903  view.on("click", function(vw, index, node, e){
24904  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24905  });
24906
24907  // load XML data
24908  dataModel.load("foobar.xml");
24909  </code></pre>
24910  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24911  * <br><br>
24912  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24913  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24914  * 
24915  * Note: old style constructor is still suported (container, template, config)
24916  * 
24917  * @constructor
24918  * Create a new View
24919  * @param {Object} config The config object
24920  * 
24921  */
24922 Roo.View = function(config, depreciated_tpl, depreciated_config){
24923     
24924     this.parent = false;
24925     
24926     if (typeof(depreciated_tpl) == 'undefined') {
24927         // new way.. - universal constructor.
24928         Roo.apply(this, config);
24929         this.el  = Roo.get(this.el);
24930     } else {
24931         // old format..
24932         this.el  = Roo.get(config);
24933         this.tpl = depreciated_tpl;
24934         Roo.apply(this, depreciated_config);
24935     }
24936     this.wrapEl  = this.el.wrap().wrap();
24937     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24938     
24939     
24940     if(typeof(this.tpl) == "string"){
24941         this.tpl = new Roo.Template(this.tpl);
24942     } else {
24943         // support xtype ctors..
24944         this.tpl = new Roo.factory(this.tpl, Roo);
24945     }
24946     
24947     
24948     this.tpl.compile();
24949     
24950     /** @private */
24951     this.addEvents({
24952         /**
24953          * @event beforeclick
24954          * Fires before a click is processed. Returns false to cancel the default action.
24955          * @param {Roo.View} this
24956          * @param {Number} index The index of the target node
24957          * @param {HTMLElement} node The target node
24958          * @param {Roo.EventObject} e The raw event object
24959          */
24960             "beforeclick" : true,
24961         /**
24962          * @event click
24963          * Fires when a template node is clicked.
24964          * @param {Roo.View} this
24965          * @param {Number} index The index of the target node
24966          * @param {HTMLElement} node The target node
24967          * @param {Roo.EventObject} e The raw event object
24968          */
24969             "click" : true,
24970         /**
24971          * @event dblclick
24972          * Fires when a template node is double clicked.
24973          * @param {Roo.View} this
24974          * @param {Number} index The index of the target node
24975          * @param {HTMLElement} node The target node
24976          * @param {Roo.EventObject} e The raw event object
24977          */
24978             "dblclick" : true,
24979         /**
24980          * @event contextmenu
24981          * Fires when a template node is right clicked.
24982          * @param {Roo.View} this
24983          * @param {Number} index The index of the target node
24984          * @param {HTMLElement} node The target node
24985          * @param {Roo.EventObject} e The raw event object
24986          */
24987             "contextmenu" : true,
24988         /**
24989          * @event selectionchange
24990          * Fires when the selected nodes change.
24991          * @param {Roo.View} this
24992          * @param {Array} selections Array of the selected nodes
24993          */
24994             "selectionchange" : true,
24995     
24996         /**
24997          * @event beforeselect
24998          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24999          * @param {Roo.View} this
25000          * @param {HTMLElement} node The node to be selected
25001          * @param {Array} selections Array of currently selected nodes
25002          */
25003             "beforeselect" : true,
25004         /**
25005          * @event preparedata
25006          * Fires on every row to render, to allow you to change the data.
25007          * @param {Roo.View} this
25008          * @param {Object} data to be rendered (change this)
25009          */
25010           "preparedata" : true
25011           
25012           
25013         });
25014
25015
25016
25017     this.el.on({
25018         "click": this.onClick,
25019         "dblclick": this.onDblClick,
25020         "contextmenu": this.onContextMenu,
25021         scope:this
25022     });
25023
25024     this.selections = [];
25025     this.nodes = [];
25026     this.cmp = new Roo.CompositeElementLite([]);
25027     if(this.store){
25028         this.store = Roo.factory(this.store, Roo.data);
25029         this.setStore(this.store, true);
25030     }
25031     
25032     if ( this.footer && this.footer.xtype) {
25033            
25034          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25035         
25036         this.footer.dataSource = this.store;
25037         this.footer.container = fctr;
25038         this.footer = Roo.factory(this.footer, Roo);
25039         fctr.insertFirst(this.el);
25040         
25041         // this is a bit insane - as the paging toolbar seems to detach the el..
25042 //        dom.parentNode.parentNode.parentNode
25043          // they get detached?
25044     }
25045     
25046     
25047     Roo.View.superclass.constructor.call(this);
25048     
25049     
25050 };
25051
25052 Roo.extend(Roo.View, Roo.util.Observable, {
25053     
25054      /**
25055      * @cfg {Roo.data.Store} store Data store to load data from.
25056      */
25057     store : false,
25058     
25059     /**
25060      * @cfg {String|Roo.Element} el The container element.
25061      */
25062     el : '',
25063     
25064     /**
25065      * @cfg {String|Roo.Template} tpl The template used by this View 
25066      */
25067     tpl : false,
25068     /**
25069      * @cfg {String} dataName the named area of the template to use as the data area
25070      *                          Works with domtemplates roo-name="name"
25071      */
25072     dataName: false,
25073     /**
25074      * @cfg {String} selectedClass The css class to add to selected nodes
25075      */
25076     selectedClass : "x-view-selected",
25077      /**
25078      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25079      */
25080     emptyText : "",
25081     
25082     /**
25083      * @cfg {String} text to display on mask (default Loading)
25084      */
25085     mask : false,
25086     /**
25087      * @cfg {Boolean} multiSelect Allow multiple selection
25088      */
25089     multiSelect : false,
25090     /**
25091      * @cfg {Boolean} singleSelect Allow single selection
25092      */
25093     singleSelect:  false,
25094     
25095     /**
25096      * @cfg {Boolean} toggleSelect - selecting 
25097      */
25098     toggleSelect : false,
25099     
25100     /**
25101      * @cfg {Boolean} tickable - selecting 
25102      */
25103     tickable : false,
25104     
25105     /**
25106      * Returns the element this view is bound to.
25107      * @return {Roo.Element}
25108      */
25109     getEl : function(){
25110         return this.wrapEl;
25111     },
25112     
25113     
25114
25115     /**
25116      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25117      */
25118     refresh : function(){
25119         //Roo.log('refresh');
25120         var t = this.tpl;
25121         
25122         // if we are using something like 'domtemplate', then
25123         // the what gets used is:
25124         // t.applySubtemplate(NAME, data, wrapping data..)
25125         // the outer template then get' applied with
25126         //     the store 'extra data'
25127         // and the body get's added to the
25128         //      roo-name="data" node?
25129         //      <span class='roo-tpl-{name}'></span> ?????
25130         
25131         
25132         
25133         this.clearSelections();
25134         this.el.update("");
25135         var html = [];
25136         var records = this.store.getRange();
25137         if(records.length < 1) {
25138             
25139             // is this valid??  = should it render a template??
25140             
25141             this.el.update(this.emptyText);
25142             return;
25143         }
25144         var el = this.el;
25145         if (this.dataName) {
25146             this.el.update(t.apply(this.store.meta)); //????
25147             el = this.el.child('.roo-tpl-' + this.dataName);
25148         }
25149         
25150         for(var i = 0, len = records.length; i < len; i++){
25151             var data = this.prepareData(records[i].data, i, records[i]);
25152             this.fireEvent("preparedata", this, data, i, records[i]);
25153             
25154             var d = Roo.apply({}, data);
25155             
25156             if(this.tickable){
25157                 Roo.apply(d, {'roo-id' : Roo.id()});
25158                 
25159                 var _this = this;
25160             
25161                 Roo.each(this.parent.item, function(item){
25162                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25163                         return;
25164                     }
25165                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25166                 });
25167             }
25168             
25169             html[html.length] = Roo.util.Format.trim(
25170                 this.dataName ?
25171                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25172                     t.apply(d)
25173             );
25174         }
25175         
25176         
25177         
25178         el.update(html.join(""));
25179         this.nodes = el.dom.childNodes;
25180         this.updateIndexes(0);
25181     },
25182     
25183
25184     /**
25185      * Function to override to reformat the data that is sent to
25186      * the template for each node.
25187      * DEPRICATED - use the preparedata event handler.
25188      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25189      * a JSON object for an UpdateManager bound view).
25190      */
25191     prepareData : function(data, index, record)
25192     {
25193         this.fireEvent("preparedata", this, data, index, record);
25194         return data;
25195     },
25196
25197     onUpdate : function(ds, record){
25198         // Roo.log('on update');   
25199         this.clearSelections();
25200         var index = this.store.indexOf(record);
25201         var n = this.nodes[index];
25202         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25203         n.parentNode.removeChild(n);
25204         this.updateIndexes(index, index);
25205     },
25206
25207     
25208     
25209 // --------- FIXME     
25210     onAdd : function(ds, records, index)
25211     {
25212         //Roo.log(['on Add', ds, records, index] );        
25213         this.clearSelections();
25214         if(this.nodes.length == 0){
25215             this.refresh();
25216             return;
25217         }
25218         var n = this.nodes[index];
25219         for(var i = 0, len = records.length; i < len; i++){
25220             var d = this.prepareData(records[i].data, i, records[i]);
25221             if(n){
25222                 this.tpl.insertBefore(n, d);
25223             }else{
25224                 
25225                 this.tpl.append(this.el, d);
25226             }
25227         }
25228         this.updateIndexes(index);
25229     },
25230
25231     onRemove : function(ds, record, index){
25232        // Roo.log('onRemove');
25233         this.clearSelections();
25234         var el = this.dataName  ?
25235             this.el.child('.roo-tpl-' + this.dataName) :
25236             this.el; 
25237         
25238         el.dom.removeChild(this.nodes[index]);
25239         this.updateIndexes(index);
25240     },
25241
25242     /**
25243      * Refresh an individual node.
25244      * @param {Number} index
25245      */
25246     refreshNode : function(index){
25247         this.onUpdate(this.store, this.store.getAt(index));
25248     },
25249
25250     updateIndexes : function(startIndex, endIndex){
25251         var ns = this.nodes;
25252         startIndex = startIndex || 0;
25253         endIndex = endIndex || ns.length - 1;
25254         for(var i = startIndex; i <= endIndex; i++){
25255             ns[i].nodeIndex = i;
25256         }
25257     },
25258
25259     /**
25260      * Changes the data store this view uses and refresh the view.
25261      * @param {Store} store
25262      */
25263     setStore : function(store, initial){
25264         if(!initial && this.store){
25265             this.store.un("datachanged", this.refresh);
25266             this.store.un("add", this.onAdd);
25267             this.store.un("remove", this.onRemove);
25268             this.store.un("update", this.onUpdate);
25269             this.store.un("clear", this.refresh);
25270             this.store.un("beforeload", this.onBeforeLoad);
25271             this.store.un("load", this.onLoad);
25272             this.store.un("loadexception", this.onLoad);
25273         }
25274         if(store){
25275           
25276             store.on("datachanged", this.refresh, this);
25277             store.on("add", this.onAdd, this);
25278             store.on("remove", this.onRemove, this);
25279             store.on("update", this.onUpdate, this);
25280             store.on("clear", this.refresh, this);
25281             store.on("beforeload", this.onBeforeLoad, this);
25282             store.on("load", this.onLoad, this);
25283             store.on("loadexception", this.onLoad, this);
25284         }
25285         
25286         if(store){
25287             this.refresh();
25288         }
25289     },
25290     /**
25291      * onbeforeLoad - masks the loading area.
25292      *
25293      */
25294     onBeforeLoad : function(store,opts)
25295     {
25296          //Roo.log('onBeforeLoad');   
25297         if (!opts.add) {
25298             this.el.update("");
25299         }
25300         this.el.mask(this.mask ? this.mask : "Loading" ); 
25301     },
25302     onLoad : function ()
25303     {
25304         this.el.unmask();
25305     },
25306     
25307
25308     /**
25309      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25310      * @param {HTMLElement} node
25311      * @return {HTMLElement} The template node
25312      */
25313     findItemFromChild : function(node){
25314         var el = this.dataName  ?
25315             this.el.child('.roo-tpl-' + this.dataName,true) :
25316             this.el.dom; 
25317         
25318         if(!node || node.parentNode == el){
25319                     return node;
25320             }
25321             var p = node.parentNode;
25322             while(p && p != el){
25323             if(p.parentNode == el){
25324                 return p;
25325             }
25326             p = p.parentNode;
25327         }
25328             return null;
25329     },
25330
25331     /** @ignore */
25332     onClick : function(e){
25333         var item = this.findItemFromChild(e.getTarget());
25334         if(item){
25335             var index = this.indexOf(item);
25336             if(this.onItemClick(item, index, e) !== false){
25337                 this.fireEvent("click", this, index, item, e);
25338             }
25339         }else{
25340             this.clearSelections();
25341         }
25342     },
25343
25344     /** @ignore */
25345     onContextMenu : function(e){
25346         var item = this.findItemFromChild(e.getTarget());
25347         if(item){
25348             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25349         }
25350     },
25351
25352     /** @ignore */
25353     onDblClick : function(e){
25354         var item = this.findItemFromChild(e.getTarget());
25355         if(item){
25356             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25357         }
25358     },
25359
25360     onItemClick : function(item, index, e)
25361     {
25362         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25363             return false;
25364         }
25365         if (this.toggleSelect) {
25366             var m = this.isSelected(item) ? 'unselect' : 'select';
25367             //Roo.log(m);
25368             var _t = this;
25369             _t[m](item, true, false);
25370             return true;
25371         }
25372         if(this.multiSelect || this.singleSelect){
25373             if(this.multiSelect && e.shiftKey && this.lastSelection){
25374                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25375             }else{
25376                 this.select(item, this.multiSelect && e.ctrlKey);
25377                 this.lastSelection = item;
25378             }
25379             
25380             if(!this.tickable){
25381                 e.preventDefault();
25382             }
25383             
25384         }
25385         return true;
25386     },
25387
25388     /**
25389      * Get the number of selected nodes.
25390      * @return {Number}
25391      */
25392     getSelectionCount : function(){
25393         return this.selections.length;
25394     },
25395
25396     /**
25397      * Get the currently selected nodes.
25398      * @return {Array} An array of HTMLElements
25399      */
25400     getSelectedNodes : function(){
25401         return this.selections;
25402     },
25403
25404     /**
25405      * Get the indexes of the selected nodes.
25406      * @return {Array}
25407      */
25408     getSelectedIndexes : function(){
25409         var indexes = [], s = this.selections;
25410         for(var i = 0, len = s.length; i < len; i++){
25411             indexes.push(s[i].nodeIndex);
25412         }
25413         return indexes;
25414     },
25415
25416     /**
25417      * Clear all selections
25418      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25419      */
25420     clearSelections : function(suppressEvent){
25421         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25422             this.cmp.elements = this.selections;
25423             this.cmp.removeClass(this.selectedClass);
25424             this.selections = [];
25425             if(!suppressEvent){
25426                 this.fireEvent("selectionchange", this, this.selections);
25427             }
25428         }
25429     },
25430
25431     /**
25432      * Returns true if the passed node is selected
25433      * @param {HTMLElement/Number} node The node or node index
25434      * @return {Boolean}
25435      */
25436     isSelected : function(node){
25437         var s = this.selections;
25438         if(s.length < 1){
25439             return false;
25440         }
25441         node = this.getNode(node);
25442         return s.indexOf(node) !== -1;
25443     },
25444
25445     /**
25446      * Selects nodes.
25447      * @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
25448      * @param {Boolean} keepExisting (optional) true to keep existing selections
25449      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25450      */
25451     select : function(nodeInfo, keepExisting, suppressEvent){
25452         if(nodeInfo instanceof Array){
25453             if(!keepExisting){
25454                 this.clearSelections(true);
25455             }
25456             for(var i = 0, len = nodeInfo.length; i < len; i++){
25457                 this.select(nodeInfo[i], true, true);
25458             }
25459             return;
25460         } 
25461         var node = this.getNode(nodeInfo);
25462         if(!node || this.isSelected(node)){
25463             return; // already selected.
25464         }
25465         if(!keepExisting){
25466             this.clearSelections(true);
25467         }
25468         
25469         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25470             Roo.fly(node).addClass(this.selectedClass);
25471             this.selections.push(node);
25472             if(!suppressEvent){
25473                 this.fireEvent("selectionchange", this, this.selections);
25474             }
25475         }
25476         
25477         
25478     },
25479       /**
25480      * Unselects nodes.
25481      * @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
25482      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25483      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25484      */
25485     unselect : function(nodeInfo, keepExisting, suppressEvent)
25486     {
25487         if(nodeInfo instanceof Array){
25488             Roo.each(this.selections, function(s) {
25489                 this.unselect(s, nodeInfo);
25490             }, this);
25491             return;
25492         }
25493         var node = this.getNode(nodeInfo);
25494         if(!node || !this.isSelected(node)){
25495             //Roo.log("not selected");
25496             return; // not selected.
25497         }
25498         // fireevent???
25499         var ns = [];
25500         Roo.each(this.selections, function(s) {
25501             if (s == node ) {
25502                 Roo.fly(node).removeClass(this.selectedClass);
25503
25504                 return;
25505             }
25506             ns.push(s);
25507         },this);
25508         
25509         this.selections= ns;
25510         this.fireEvent("selectionchange", this, this.selections);
25511     },
25512
25513     /**
25514      * Gets a template node.
25515      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25516      * @return {HTMLElement} The node or null if it wasn't found
25517      */
25518     getNode : function(nodeInfo){
25519         if(typeof nodeInfo == "string"){
25520             return document.getElementById(nodeInfo);
25521         }else if(typeof nodeInfo == "number"){
25522             return this.nodes[nodeInfo];
25523         }
25524         return nodeInfo;
25525     },
25526
25527     /**
25528      * Gets a range template nodes.
25529      * @param {Number} startIndex
25530      * @param {Number} endIndex
25531      * @return {Array} An array of nodes
25532      */
25533     getNodes : function(start, end){
25534         var ns = this.nodes;
25535         start = start || 0;
25536         end = typeof end == "undefined" ? ns.length - 1 : end;
25537         var nodes = [];
25538         if(start <= end){
25539             for(var i = start; i <= end; i++){
25540                 nodes.push(ns[i]);
25541             }
25542         } else{
25543             for(var i = start; i >= end; i--){
25544                 nodes.push(ns[i]);
25545             }
25546         }
25547         return nodes;
25548     },
25549
25550     /**
25551      * Finds the index of the passed node
25552      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25553      * @return {Number} The index of the node or -1
25554      */
25555     indexOf : function(node){
25556         node = this.getNode(node);
25557         if(typeof node.nodeIndex == "number"){
25558             return node.nodeIndex;
25559         }
25560         var ns = this.nodes;
25561         for(var i = 0, len = ns.length; i < len; i++){
25562             if(ns[i] == node){
25563                 return i;
25564             }
25565         }
25566         return -1;
25567     }
25568 });
25569 /*
25570  * Based on:
25571  * Ext JS Library 1.1.1
25572  * Copyright(c) 2006-2007, Ext JS, LLC.
25573  *
25574  * Originally Released Under LGPL - original licence link has changed is not relivant.
25575  *
25576  * Fork - LGPL
25577  * <script type="text/javascript">
25578  */
25579
25580 /**
25581  * @class Roo.JsonView
25582  * @extends Roo.View
25583  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25584 <pre><code>
25585 var view = new Roo.JsonView({
25586     container: "my-element",
25587     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25588     multiSelect: true, 
25589     jsonRoot: "data" 
25590 });
25591
25592 // listen for node click?
25593 view.on("click", function(vw, index, node, e){
25594     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25595 });
25596
25597 // direct load of JSON data
25598 view.load("foobar.php");
25599
25600 // Example from my blog list
25601 var tpl = new Roo.Template(
25602     '&lt;div class="entry"&gt;' +
25603     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25604     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25605     "&lt;/div&gt;&lt;hr /&gt;"
25606 );
25607
25608 var moreView = new Roo.JsonView({
25609     container :  "entry-list", 
25610     template : tpl,
25611     jsonRoot: "posts"
25612 });
25613 moreView.on("beforerender", this.sortEntries, this);
25614 moreView.load({
25615     url: "/blog/get-posts.php",
25616     params: "allposts=true",
25617     text: "Loading Blog Entries..."
25618 });
25619 </code></pre>
25620
25621 * Note: old code is supported with arguments : (container, template, config)
25622
25623
25624  * @constructor
25625  * Create a new JsonView
25626  * 
25627  * @param {Object} config The config object
25628  * 
25629  */
25630 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25631     
25632     
25633     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25634
25635     var um = this.el.getUpdateManager();
25636     um.setRenderer(this);
25637     um.on("update", this.onLoad, this);
25638     um.on("failure", this.onLoadException, this);
25639
25640     /**
25641      * @event beforerender
25642      * Fires before rendering of the downloaded JSON data.
25643      * @param {Roo.JsonView} this
25644      * @param {Object} data The JSON data loaded
25645      */
25646     /**
25647      * @event load
25648      * Fires when data is loaded.
25649      * @param {Roo.JsonView} this
25650      * @param {Object} data The JSON data loaded
25651      * @param {Object} response The raw Connect response object
25652      */
25653     /**
25654      * @event loadexception
25655      * Fires when loading fails.
25656      * @param {Roo.JsonView} this
25657      * @param {Object} response The raw Connect response object
25658      */
25659     this.addEvents({
25660         'beforerender' : true,
25661         'load' : true,
25662         'loadexception' : true
25663     });
25664 };
25665 Roo.extend(Roo.JsonView, Roo.View, {
25666     /**
25667      * @type {String} The root property in the loaded JSON object that contains the data
25668      */
25669     jsonRoot : "",
25670
25671     /**
25672      * Refreshes the view.
25673      */
25674     refresh : function(){
25675         this.clearSelections();
25676         this.el.update("");
25677         var html = [];
25678         var o = this.jsonData;
25679         if(o && o.length > 0){
25680             for(var i = 0, len = o.length; i < len; i++){
25681                 var data = this.prepareData(o[i], i, o);
25682                 html[html.length] = this.tpl.apply(data);
25683             }
25684         }else{
25685             html.push(this.emptyText);
25686         }
25687         this.el.update(html.join(""));
25688         this.nodes = this.el.dom.childNodes;
25689         this.updateIndexes(0);
25690     },
25691
25692     /**
25693      * 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.
25694      * @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:
25695      <pre><code>
25696      view.load({
25697          url: "your-url.php",
25698          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25699          callback: yourFunction,
25700          scope: yourObject, //(optional scope)
25701          discardUrl: false,
25702          nocache: false,
25703          text: "Loading...",
25704          timeout: 30,
25705          scripts: false
25706      });
25707      </code></pre>
25708      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25709      * 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.
25710      * @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}
25711      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25712      * @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.
25713      */
25714     load : function(){
25715         var um = this.el.getUpdateManager();
25716         um.update.apply(um, arguments);
25717     },
25718
25719     render : function(el, response){
25720         this.clearSelections();
25721         this.el.update("");
25722         var o;
25723         try{
25724             o = Roo.util.JSON.decode(response.responseText);
25725             if(this.jsonRoot){
25726                 
25727                 o = o[this.jsonRoot];
25728             }
25729         } catch(e){
25730         }
25731         /**
25732          * The current JSON data or null
25733          */
25734         this.jsonData = o;
25735         this.beforeRender();
25736         this.refresh();
25737     },
25738
25739 /**
25740  * Get the number of records in the current JSON dataset
25741  * @return {Number}
25742  */
25743     getCount : function(){
25744         return this.jsonData ? this.jsonData.length : 0;
25745     },
25746
25747 /**
25748  * Returns the JSON object for the specified node(s)
25749  * @param {HTMLElement/Array} node The node or an array of nodes
25750  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25751  * you get the JSON object for the node
25752  */
25753     getNodeData : function(node){
25754         if(node instanceof Array){
25755             var data = [];
25756             for(var i = 0, len = node.length; i < len; i++){
25757                 data.push(this.getNodeData(node[i]));
25758             }
25759             return data;
25760         }
25761         return this.jsonData[this.indexOf(node)] || null;
25762     },
25763
25764     beforeRender : function(){
25765         this.snapshot = this.jsonData;
25766         if(this.sortInfo){
25767             this.sort.apply(this, this.sortInfo);
25768         }
25769         this.fireEvent("beforerender", this, this.jsonData);
25770     },
25771
25772     onLoad : function(el, o){
25773         this.fireEvent("load", this, this.jsonData, o);
25774     },
25775
25776     onLoadException : function(el, o){
25777         this.fireEvent("loadexception", this, o);
25778     },
25779
25780 /**
25781  * Filter the data by a specific property.
25782  * @param {String} property A property on your JSON objects
25783  * @param {String/RegExp} value Either string that the property values
25784  * should start with, or a RegExp to test against the property
25785  */
25786     filter : function(property, value){
25787         if(this.jsonData){
25788             var data = [];
25789             var ss = this.snapshot;
25790             if(typeof value == "string"){
25791                 var vlen = value.length;
25792                 if(vlen == 0){
25793                     this.clearFilter();
25794                     return;
25795                 }
25796                 value = value.toLowerCase();
25797                 for(var i = 0, len = ss.length; i < len; i++){
25798                     var o = ss[i];
25799                     if(o[property].substr(0, vlen).toLowerCase() == value){
25800                         data.push(o);
25801                     }
25802                 }
25803             } else if(value.exec){ // regex?
25804                 for(var i = 0, len = ss.length; i < len; i++){
25805                     var o = ss[i];
25806                     if(value.test(o[property])){
25807                         data.push(o);
25808                     }
25809                 }
25810             } else{
25811                 return;
25812             }
25813             this.jsonData = data;
25814             this.refresh();
25815         }
25816     },
25817
25818 /**
25819  * Filter by a function. The passed function will be called with each
25820  * object in the current dataset. If the function returns true the value is kept,
25821  * otherwise it is filtered.
25822  * @param {Function} fn
25823  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25824  */
25825     filterBy : function(fn, scope){
25826         if(this.jsonData){
25827             var data = [];
25828             var ss = this.snapshot;
25829             for(var i = 0, len = ss.length; i < len; i++){
25830                 var o = ss[i];
25831                 if(fn.call(scope || this, o)){
25832                     data.push(o);
25833                 }
25834             }
25835             this.jsonData = data;
25836             this.refresh();
25837         }
25838     },
25839
25840 /**
25841  * Clears the current filter.
25842  */
25843     clearFilter : function(){
25844         if(this.snapshot && this.jsonData != this.snapshot){
25845             this.jsonData = this.snapshot;
25846             this.refresh();
25847         }
25848     },
25849
25850
25851 /**
25852  * Sorts the data for this view and refreshes it.
25853  * @param {String} property A property on your JSON objects to sort on
25854  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25855  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25856  */
25857     sort : function(property, dir, sortType){
25858         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25859         if(this.jsonData){
25860             var p = property;
25861             var dsc = dir && dir.toLowerCase() == "desc";
25862             var f = function(o1, o2){
25863                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25864                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25865                 ;
25866                 if(v1 < v2){
25867                     return dsc ? +1 : -1;
25868                 } else if(v1 > v2){
25869                     return dsc ? -1 : +1;
25870                 } else{
25871                     return 0;
25872                 }
25873             };
25874             this.jsonData.sort(f);
25875             this.refresh();
25876             if(this.jsonData != this.snapshot){
25877                 this.snapshot.sort(f);
25878             }
25879         }
25880     }
25881 });/*
25882  * Based on:
25883  * Ext JS Library 1.1.1
25884  * Copyright(c) 2006-2007, Ext JS, LLC.
25885  *
25886  * Originally Released Under LGPL - original licence link has changed is not relivant.
25887  *
25888  * Fork - LGPL
25889  * <script type="text/javascript">
25890  */
25891  
25892
25893 /**
25894  * @class Roo.ColorPalette
25895  * @extends Roo.Component
25896  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25897  * Here's an example of typical usage:
25898  * <pre><code>
25899 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25900 cp.render('my-div');
25901
25902 cp.on('select', function(palette, selColor){
25903     // do something with selColor
25904 });
25905 </code></pre>
25906  * @constructor
25907  * Create a new ColorPalette
25908  * @param {Object} config The config object
25909  */
25910 Roo.ColorPalette = function(config){
25911     Roo.ColorPalette.superclass.constructor.call(this, config);
25912     this.addEvents({
25913         /**
25914              * @event select
25915              * Fires when a color is selected
25916              * @param {ColorPalette} this
25917              * @param {String} color The 6-digit color hex code (without the # symbol)
25918              */
25919         select: true
25920     });
25921
25922     if(this.handler){
25923         this.on("select", this.handler, this.scope, true);
25924     }
25925 };
25926 Roo.extend(Roo.ColorPalette, Roo.Component, {
25927     /**
25928      * @cfg {String} itemCls
25929      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25930      */
25931     itemCls : "x-color-palette",
25932     /**
25933      * @cfg {String} value
25934      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25935      * the hex codes are case-sensitive.
25936      */
25937     value : null,
25938     clickEvent:'click',
25939     // private
25940     ctype: "Roo.ColorPalette",
25941
25942     /**
25943      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25944      */
25945     allowReselect : false,
25946
25947     /**
25948      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25949      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25950      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25951      * of colors with the width setting until the box is symmetrical.</p>
25952      * <p>You can override individual colors if needed:</p>
25953      * <pre><code>
25954 var cp = new Roo.ColorPalette();
25955 cp.colors[0] = "FF0000";  // change the first box to red
25956 </code></pre>
25957
25958 Or you can provide a custom array of your own for complete control:
25959 <pre><code>
25960 var cp = new Roo.ColorPalette();
25961 cp.colors = ["000000", "993300", "333300"];
25962 </code></pre>
25963      * @type Array
25964      */
25965     colors : [
25966         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25967         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25968         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25969         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25970         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25971     ],
25972
25973     // private
25974     onRender : function(container, position){
25975         var t = new Roo.MasterTemplate(
25976             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25977         );
25978         var c = this.colors;
25979         for(var i = 0, len = c.length; i < len; i++){
25980             t.add([c[i]]);
25981         }
25982         var el = document.createElement("div");
25983         el.className = this.itemCls;
25984         t.overwrite(el);
25985         container.dom.insertBefore(el, position);
25986         this.el = Roo.get(el);
25987         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25988         if(this.clickEvent != 'click'){
25989             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25990         }
25991     },
25992
25993     // private
25994     afterRender : function(){
25995         Roo.ColorPalette.superclass.afterRender.call(this);
25996         if(this.value){
25997             var s = this.value;
25998             this.value = null;
25999             this.select(s);
26000         }
26001     },
26002
26003     // private
26004     handleClick : function(e, t){
26005         e.preventDefault();
26006         if(!this.disabled){
26007             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
26008             this.select(c.toUpperCase());
26009         }
26010     },
26011
26012     /**
26013      * Selects the specified color in the palette (fires the select event)
26014      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
26015      */
26016     select : function(color){
26017         color = color.replace("#", "");
26018         if(color != this.value || this.allowReselect){
26019             var el = this.el;
26020             if(this.value){
26021                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26022             }
26023             el.child("a.color-"+color).addClass("x-color-palette-sel");
26024             this.value = color;
26025             this.fireEvent("select", this, color);
26026         }
26027     }
26028 });/*
26029  * Based on:
26030  * Ext JS Library 1.1.1
26031  * Copyright(c) 2006-2007, Ext JS, LLC.
26032  *
26033  * Originally Released Under LGPL - original licence link has changed is not relivant.
26034  *
26035  * Fork - LGPL
26036  * <script type="text/javascript">
26037  */
26038  
26039 /**
26040  * @class Roo.DatePicker
26041  * @extends Roo.Component
26042  * Simple date picker class.
26043  * @constructor
26044  * Create a new DatePicker
26045  * @param {Object} config The config object
26046  */
26047 Roo.DatePicker = function(config){
26048     Roo.DatePicker.superclass.constructor.call(this, config);
26049
26050     this.value = config && config.value ?
26051                  config.value.clearTime() : new Date().clearTime();
26052
26053     this.addEvents({
26054         /**
26055              * @event select
26056              * Fires when a date is selected
26057              * @param {DatePicker} this
26058              * @param {Date} date The selected date
26059              */
26060         'select': true,
26061         /**
26062              * @event monthchange
26063              * Fires when the displayed month changes 
26064              * @param {DatePicker} this
26065              * @param {Date} date The selected month
26066              */
26067         'monthchange': true
26068     });
26069
26070     if(this.handler){
26071         this.on("select", this.handler,  this.scope || this);
26072     }
26073     // build the disabledDatesRE
26074     if(!this.disabledDatesRE && this.disabledDates){
26075         var dd = this.disabledDates;
26076         var re = "(?:";
26077         for(var i = 0; i < dd.length; i++){
26078             re += dd[i];
26079             if(i != dd.length-1) {
26080                 re += "|";
26081             }
26082         }
26083         this.disabledDatesRE = new RegExp(re + ")");
26084     }
26085 };
26086
26087 Roo.extend(Roo.DatePicker, Roo.Component, {
26088     /**
26089      * @cfg {String} todayText
26090      * The text to display on the button that selects the current date (defaults to "Today")
26091      */
26092     todayText : "Today",
26093     /**
26094      * @cfg {String} okText
26095      * The text to display on the ok button
26096      */
26097     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26098     /**
26099      * @cfg {String} cancelText
26100      * The text to display on the cancel button
26101      */
26102     cancelText : "Cancel",
26103     /**
26104      * @cfg {String} todayTip
26105      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26106      */
26107     todayTip : "{0} (Spacebar)",
26108     /**
26109      * @cfg {Date} minDate
26110      * Minimum allowable date (JavaScript date object, defaults to null)
26111      */
26112     minDate : null,
26113     /**
26114      * @cfg {Date} maxDate
26115      * Maximum allowable date (JavaScript date object, defaults to null)
26116      */
26117     maxDate : null,
26118     /**
26119      * @cfg {String} minText
26120      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26121      */
26122     minText : "This date is before the minimum date",
26123     /**
26124      * @cfg {String} maxText
26125      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26126      */
26127     maxText : "This date is after the maximum date",
26128     /**
26129      * @cfg {String} format
26130      * The default date format string which can be overriden for localization support.  The format must be
26131      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26132      */
26133     format : "m/d/y",
26134     /**
26135      * @cfg {Array} disabledDays
26136      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26137      */
26138     disabledDays : null,
26139     /**
26140      * @cfg {String} disabledDaysText
26141      * The tooltip to display when the date falls on a disabled day (defaults to "")
26142      */
26143     disabledDaysText : "",
26144     /**
26145      * @cfg {RegExp} disabledDatesRE
26146      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26147      */
26148     disabledDatesRE : null,
26149     /**
26150      * @cfg {String} disabledDatesText
26151      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26152      */
26153     disabledDatesText : "",
26154     /**
26155      * @cfg {Boolean} constrainToViewport
26156      * True to constrain the date picker to the viewport (defaults to true)
26157      */
26158     constrainToViewport : true,
26159     /**
26160      * @cfg {Array} monthNames
26161      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26162      */
26163     monthNames : Date.monthNames,
26164     /**
26165      * @cfg {Array} dayNames
26166      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26167      */
26168     dayNames : Date.dayNames,
26169     /**
26170      * @cfg {String} nextText
26171      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26172      */
26173     nextText: 'Next Month (Control+Right)',
26174     /**
26175      * @cfg {String} prevText
26176      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26177      */
26178     prevText: 'Previous Month (Control+Left)',
26179     /**
26180      * @cfg {String} monthYearText
26181      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26182      */
26183     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26184     /**
26185      * @cfg {Number} startDay
26186      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26187      */
26188     startDay : 0,
26189     /**
26190      * @cfg {Bool} showClear
26191      * Show a clear button (usefull for date form elements that can be blank.)
26192      */
26193     
26194     showClear: false,
26195     
26196     /**
26197      * Sets the value of the date field
26198      * @param {Date} value The date to set
26199      */
26200     setValue : function(value){
26201         var old = this.value;
26202         
26203         if (typeof(value) == 'string') {
26204          
26205             value = Date.parseDate(value, this.format);
26206         }
26207         if (!value) {
26208             value = new Date();
26209         }
26210         
26211         this.value = value.clearTime(true);
26212         if(this.el){
26213             this.update(this.value);
26214         }
26215     },
26216
26217     /**
26218      * Gets the current selected value of the date field
26219      * @return {Date} The selected date
26220      */
26221     getValue : function(){
26222         return this.value;
26223     },
26224
26225     // private
26226     focus : function(){
26227         if(this.el){
26228             this.update(this.activeDate);
26229         }
26230     },
26231
26232     // privateval
26233     onRender : function(container, position){
26234         
26235         var m = [
26236              '<table cellspacing="0">',
26237                 '<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>',
26238                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26239         var dn = this.dayNames;
26240         for(var i = 0; i < 7; i++){
26241             var d = this.startDay+i;
26242             if(d > 6){
26243                 d = d-7;
26244             }
26245             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26246         }
26247         m[m.length] = "</tr></thead><tbody><tr>";
26248         for(var i = 0; i < 42; i++) {
26249             if(i % 7 == 0 && i != 0){
26250                 m[m.length] = "</tr><tr>";
26251             }
26252             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26253         }
26254         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26255             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26256
26257         var el = document.createElement("div");
26258         el.className = "x-date-picker";
26259         el.innerHTML = m.join("");
26260
26261         container.dom.insertBefore(el, position);
26262
26263         this.el = Roo.get(el);
26264         this.eventEl = Roo.get(el.firstChild);
26265
26266         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26267             handler: this.showPrevMonth,
26268             scope: this,
26269             preventDefault:true,
26270             stopDefault:true
26271         });
26272
26273         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26274             handler: this.showNextMonth,
26275             scope: this,
26276             preventDefault:true,
26277             stopDefault:true
26278         });
26279
26280         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26281
26282         this.monthPicker = this.el.down('div.x-date-mp');
26283         this.monthPicker.enableDisplayMode('block');
26284         
26285         var kn = new Roo.KeyNav(this.eventEl, {
26286             "left" : function(e){
26287                 e.ctrlKey ?
26288                     this.showPrevMonth() :
26289                     this.update(this.activeDate.add("d", -1));
26290             },
26291
26292             "right" : function(e){
26293                 e.ctrlKey ?
26294                     this.showNextMonth() :
26295                     this.update(this.activeDate.add("d", 1));
26296             },
26297
26298             "up" : function(e){
26299                 e.ctrlKey ?
26300                     this.showNextYear() :
26301                     this.update(this.activeDate.add("d", -7));
26302             },
26303
26304             "down" : function(e){
26305                 e.ctrlKey ?
26306                     this.showPrevYear() :
26307                     this.update(this.activeDate.add("d", 7));
26308             },
26309
26310             "pageUp" : function(e){
26311                 this.showNextMonth();
26312             },
26313
26314             "pageDown" : function(e){
26315                 this.showPrevMonth();
26316             },
26317
26318             "enter" : function(e){
26319                 e.stopPropagation();
26320                 return true;
26321             },
26322
26323             scope : this
26324         });
26325
26326         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26327
26328         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26329
26330         this.el.unselectable();
26331         
26332         this.cells = this.el.select("table.x-date-inner tbody td");
26333         this.textNodes = this.el.query("table.x-date-inner tbody span");
26334
26335         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26336             text: "&#160;",
26337             tooltip: this.monthYearText
26338         });
26339
26340         this.mbtn.on('click', this.showMonthPicker, this);
26341         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26342
26343
26344         var today = (new Date()).dateFormat(this.format);
26345         
26346         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26347         if (this.showClear) {
26348             baseTb.add( new Roo.Toolbar.Fill());
26349         }
26350         baseTb.add({
26351             text: String.format(this.todayText, today),
26352             tooltip: String.format(this.todayTip, today),
26353             handler: this.selectToday,
26354             scope: this
26355         });
26356         
26357         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26358             
26359         //});
26360         if (this.showClear) {
26361             
26362             baseTb.add( new Roo.Toolbar.Fill());
26363             baseTb.add({
26364                 text: '&#160;',
26365                 cls: 'x-btn-icon x-btn-clear',
26366                 handler: function() {
26367                     //this.value = '';
26368                     this.fireEvent("select", this, '');
26369                 },
26370                 scope: this
26371             });
26372         }
26373         
26374         
26375         if(Roo.isIE){
26376             this.el.repaint();
26377         }
26378         this.update(this.value);
26379     },
26380
26381     createMonthPicker : function(){
26382         if(!this.monthPicker.dom.firstChild){
26383             var buf = ['<table border="0" cellspacing="0">'];
26384             for(var i = 0; i < 6; i++){
26385                 buf.push(
26386                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26387                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26388                     i == 0 ?
26389                     '<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>' :
26390                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26391                 );
26392             }
26393             buf.push(
26394                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26395                     this.okText,
26396                     '</button><button type="button" class="x-date-mp-cancel">',
26397                     this.cancelText,
26398                     '</button></td></tr>',
26399                 '</table>'
26400             );
26401             this.monthPicker.update(buf.join(''));
26402             this.monthPicker.on('click', this.onMonthClick, this);
26403             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26404
26405             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26406             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26407
26408             this.mpMonths.each(function(m, a, i){
26409                 i += 1;
26410                 if((i%2) == 0){
26411                     m.dom.xmonth = 5 + Math.round(i * .5);
26412                 }else{
26413                     m.dom.xmonth = Math.round((i-1) * .5);
26414                 }
26415             });
26416         }
26417     },
26418
26419     showMonthPicker : function(){
26420         this.createMonthPicker();
26421         var size = this.el.getSize();
26422         this.monthPicker.setSize(size);
26423         this.monthPicker.child('table').setSize(size);
26424
26425         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26426         this.updateMPMonth(this.mpSelMonth);
26427         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26428         this.updateMPYear(this.mpSelYear);
26429
26430         this.monthPicker.slideIn('t', {duration:.2});
26431     },
26432
26433     updateMPYear : function(y){
26434         this.mpyear = y;
26435         var ys = this.mpYears.elements;
26436         for(var i = 1; i <= 10; i++){
26437             var td = ys[i-1], y2;
26438             if((i%2) == 0){
26439                 y2 = y + Math.round(i * .5);
26440                 td.firstChild.innerHTML = y2;
26441                 td.xyear = y2;
26442             }else{
26443                 y2 = y - (5-Math.round(i * .5));
26444                 td.firstChild.innerHTML = y2;
26445                 td.xyear = y2;
26446             }
26447             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26448         }
26449     },
26450
26451     updateMPMonth : function(sm){
26452         this.mpMonths.each(function(m, a, i){
26453             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26454         });
26455     },
26456
26457     selectMPMonth: function(m){
26458         
26459     },
26460
26461     onMonthClick : function(e, t){
26462         e.stopEvent();
26463         var el = new Roo.Element(t), pn;
26464         if(el.is('button.x-date-mp-cancel')){
26465             this.hideMonthPicker();
26466         }
26467         else if(el.is('button.x-date-mp-ok')){
26468             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26469             this.hideMonthPicker();
26470         }
26471         else if(pn = el.up('td.x-date-mp-month', 2)){
26472             this.mpMonths.removeClass('x-date-mp-sel');
26473             pn.addClass('x-date-mp-sel');
26474             this.mpSelMonth = pn.dom.xmonth;
26475         }
26476         else if(pn = el.up('td.x-date-mp-year', 2)){
26477             this.mpYears.removeClass('x-date-mp-sel');
26478             pn.addClass('x-date-mp-sel');
26479             this.mpSelYear = pn.dom.xyear;
26480         }
26481         else if(el.is('a.x-date-mp-prev')){
26482             this.updateMPYear(this.mpyear-10);
26483         }
26484         else if(el.is('a.x-date-mp-next')){
26485             this.updateMPYear(this.mpyear+10);
26486         }
26487     },
26488
26489     onMonthDblClick : function(e, t){
26490         e.stopEvent();
26491         var el = new Roo.Element(t), pn;
26492         if(pn = el.up('td.x-date-mp-month', 2)){
26493             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26494             this.hideMonthPicker();
26495         }
26496         else if(pn = el.up('td.x-date-mp-year', 2)){
26497             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26498             this.hideMonthPicker();
26499         }
26500     },
26501
26502     hideMonthPicker : function(disableAnim){
26503         if(this.monthPicker){
26504             if(disableAnim === true){
26505                 this.monthPicker.hide();
26506             }else{
26507                 this.monthPicker.slideOut('t', {duration:.2});
26508             }
26509         }
26510     },
26511
26512     // private
26513     showPrevMonth : function(e){
26514         this.update(this.activeDate.add("mo", -1));
26515     },
26516
26517     // private
26518     showNextMonth : function(e){
26519         this.update(this.activeDate.add("mo", 1));
26520     },
26521
26522     // private
26523     showPrevYear : function(){
26524         this.update(this.activeDate.add("y", -1));
26525     },
26526
26527     // private
26528     showNextYear : function(){
26529         this.update(this.activeDate.add("y", 1));
26530     },
26531
26532     // private
26533     handleMouseWheel : function(e){
26534         var delta = e.getWheelDelta();
26535         if(delta > 0){
26536             this.showPrevMonth();
26537             e.stopEvent();
26538         } else if(delta < 0){
26539             this.showNextMonth();
26540             e.stopEvent();
26541         }
26542     },
26543
26544     // private
26545     handleDateClick : function(e, t){
26546         e.stopEvent();
26547         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26548             this.setValue(new Date(t.dateValue));
26549             this.fireEvent("select", this, this.value);
26550         }
26551     },
26552
26553     // private
26554     selectToday : function(){
26555         this.setValue(new Date().clearTime());
26556         this.fireEvent("select", this, this.value);
26557     },
26558
26559     // private
26560     update : function(date)
26561     {
26562         var vd = this.activeDate;
26563         this.activeDate = date;
26564         if(vd && this.el){
26565             var t = date.getTime();
26566             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26567                 this.cells.removeClass("x-date-selected");
26568                 this.cells.each(function(c){
26569                    if(c.dom.firstChild.dateValue == t){
26570                        c.addClass("x-date-selected");
26571                        setTimeout(function(){
26572                             try{c.dom.firstChild.focus();}catch(e){}
26573                        }, 50);
26574                        return false;
26575                    }
26576                 });
26577                 return;
26578             }
26579         }
26580         
26581         var days = date.getDaysInMonth();
26582         var firstOfMonth = date.getFirstDateOfMonth();
26583         var startingPos = firstOfMonth.getDay()-this.startDay;
26584
26585         if(startingPos <= this.startDay){
26586             startingPos += 7;
26587         }
26588
26589         var pm = date.add("mo", -1);
26590         var prevStart = pm.getDaysInMonth()-startingPos;
26591
26592         var cells = this.cells.elements;
26593         var textEls = this.textNodes;
26594         days += startingPos;
26595
26596         // convert everything to numbers so it's fast
26597         var day = 86400000;
26598         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26599         var today = new Date().clearTime().getTime();
26600         var sel = date.clearTime().getTime();
26601         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26602         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26603         var ddMatch = this.disabledDatesRE;
26604         var ddText = this.disabledDatesText;
26605         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26606         var ddaysText = this.disabledDaysText;
26607         var format = this.format;
26608
26609         var setCellClass = function(cal, cell){
26610             cell.title = "";
26611             var t = d.getTime();
26612             cell.firstChild.dateValue = t;
26613             if(t == today){
26614                 cell.className += " x-date-today";
26615                 cell.title = cal.todayText;
26616             }
26617             if(t == sel){
26618                 cell.className += " x-date-selected";
26619                 setTimeout(function(){
26620                     try{cell.firstChild.focus();}catch(e){}
26621                 }, 50);
26622             }
26623             // disabling
26624             if(t < min) {
26625                 cell.className = " x-date-disabled";
26626                 cell.title = cal.minText;
26627                 return;
26628             }
26629             if(t > max) {
26630                 cell.className = " x-date-disabled";
26631                 cell.title = cal.maxText;
26632                 return;
26633             }
26634             if(ddays){
26635                 if(ddays.indexOf(d.getDay()) != -1){
26636                     cell.title = ddaysText;
26637                     cell.className = " x-date-disabled";
26638                 }
26639             }
26640             if(ddMatch && format){
26641                 var fvalue = d.dateFormat(format);
26642                 if(ddMatch.test(fvalue)){
26643                     cell.title = ddText.replace("%0", fvalue);
26644                     cell.className = " x-date-disabled";
26645                 }
26646             }
26647         };
26648
26649         var i = 0;
26650         for(; i < startingPos; i++) {
26651             textEls[i].innerHTML = (++prevStart);
26652             d.setDate(d.getDate()+1);
26653             cells[i].className = "x-date-prevday";
26654             setCellClass(this, cells[i]);
26655         }
26656         for(; i < days; i++){
26657             intDay = i - startingPos + 1;
26658             textEls[i].innerHTML = (intDay);
26659             d.setDate(d.getDate()+1);
26660             cells[i].className = "x-date-active";
26661             setCellClass(this, cells[i]);
26662         }
26663         var extraDays = 0;
26664         for(; i < 42; i++) {
26665              textEls[i].innerHTML = (++extraDays);
26666              d.setDate(d.getDate()+1);
26667              cells[i].className = "x-date-nextday";
26668              setCellClass(this, cells[i]);
26669         }
26670
26671         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26672         this.fireEvent('monthchange', this, date);
26673         
26674         if(!this.internalRender){
26675             var main = this.el.dom.firstChild;
26676             var w = main.offsetWidth;
26677             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26678             Roo.fly(main).setWidth(w);
26679             this.internalRender = true;
26680             // opera does not respect the auto grow header center column
26681             // then, after it gets a width opera refuses to recalculate
26682             // without a second pass
26683             if(Roo.isOpera && !this.secondPass){
26684                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26685                 this.secondPass = true;
26686                 this.update.defer(10, this, [date]);
26687             }
26688         }
26689         
26690         
26691     }
26692 });        /*
26693  * Based on:
26694  * Ext JS Library 1.1.1
26695  * Copyright(c) 2006-2007, Ext JS, LLC.
26696  *
26697  * Originally Released Under LGPL - original licence link has changed is not relivant.
26698  *
26699  * Fork - LGPL
26700  * <script type="text/javascript">
26701  */
26702 /**
26703  * @class Roo.TabPanel
26704  * @extends Roo.util.Observable
26705  * A lightweight tab container.
26706  * <br><br>
26707  * Usage:
26708  * <pre><code>
26709 // basic tabs 1, built from existing content
26710 var tabs = new Roo.TabPanel("tabs1");
26711 tabs.addTab("script", "View Script");
26712 tabs.addTab("markup", "View Markup");
26713 tabs.activate("script");
26714
26715 // more advanced tabs, built from javascript
26716 var jtabs = new Roo.TabPanel("jtabs");
26717 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26718
26719 // set up the UpdateManager
26720 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26721 var updater = tab2.getUpdateManager();
26722 updater.setDefaultUrl("ajax1.htm");
26723 tab2.on('activate', updater.refresh, updater, true);
26724
26725 // Use setUrl for Ajax loading
26726 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26727 tab3.setUrl("ajax2.htm", null, true);
26728
26729 // Disabled tab
26730 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26731 tab4.disable();
26732
26733 jtabs.activate("jtabs-1");
26734  * </code></pre>
26735  * @constructor
26736  * Create a new TabPanel.
26737  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26738  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26739  */
26740 Roo.TabPanel = function(container, config){
26741     /**
26742     * The container element for this TabPanel.
26743     * @type Roo.Element
26744     */
26745     this.el = Roo.get(container, true);
26746     if(config){
26747         if(typeof config == "boolean"){
26748             this.tabPosition = config ? "bottom" : "top";
26749         }else{
26750             Roo.apply(this, config);
26751         }
26752     }
26753     if(this.tabPosition == "bottom"){
26754         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26755         this.el.addClass("x-tabs-bottom");
26756     }
26757     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26758     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26759     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26760     if(Roo.isIE){
26761         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26762     }
26763     if(this.tabPosition != "bottom"){
26764         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26765          * @type Roo.Element
26766          */
26767         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26768         this.el.addClass("x-tabs-top");
26769     }
26770     this.items = [];
26771
26772     this.bodyEl.setStyle("position", "relative");
26773
26774     this.active = null;
26775     this.activateDelegate = this.activate.createDelegate(this);
26776
26777     this.addEvents({
26778         /**
26779          * @event tabchange
26780          * Fires when the active tab changes
26781          * @param {Roo.TabPanel} this
26782          * @param {Roo.TabPanelItem} activePanel The new active tab
26783          */
26784         "tabchange": true,
26785         /**
26786          * @event beforetabchange
26787          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26788          * @param {Roo.TabPanel} this
26789          * @param {Object} e Set cancel to true on this object to cancel the tab change
26790          * @param {Roo.TabPanelItem} tab The tab being changed to
26791          */
26792         "beforetabchange" : true
26793     });
26794
26795     Roo.EventManager.onWindowResize(this.onResize, this);
26796     this.cpad = this.el.getPadding("lr");
26797     this.hiddenCount = 0;
26798
26799
26800     // toolbar on the tabbar support...
26801     if (this.toolbar) {
26802         var tcfg = this.toolbar;
26803         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26804         this.toolbar = new Roo.Toolbar(tcfg);
26805         if (Roo.isSafari) {
26806             var tbl = tcfg.container.child('table', true);
26807             tbl.setAttribute('width', '100%');
26808         }
26809         
26810     }
26811    
26812
26813
26814     Roo.TabPanel.superclass.constructor.call(this);
26815 };
26816
26817 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26818     /*
26819      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26820      */
26821     tabPosition : "top",
26822     /*
26823      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26824      */
26825     currentTabWidth : 0,
26826     /*
26827      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26828      */
26829     minTabWidth : 40,
26830     /*
26831      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26832      */
26833     maxTabWidth : 250,
26834     /*
26835      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26836      */
26837     preferredTabWidth : 175,
26838     /*
26839      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26840      */
26841     resizeTabs : false,
26842     /*
26843      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26844      */
26845     monitorResize : true,
26846     /*
26847      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26848      */
26849     toolbar : false,
26850
26851     /**
26852      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26853      * @param {String} id The id of the div to use <b>or create</b>
26854      * @param {String} text The text for the tab
26855      * @param {String} content (optional) Content to put in the TabPanelItem body
26856      * @param {Boolean} closable (optional) True to create a close icon on the tab
26857      * @return {Roo.TabPanelItem} The created TabPanelItem
26858      */
26859     addTab : function(id, text, content, closable){
26860         var item = new Roo.TabPanelItem(this, id, text, closable);
26861         this.addTabItem(item);
26862         if(content){
26863             item.setContent(content);
26864         }
26865         return item;
26866     },
26867
26868     /**
26869      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26870      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26871      * @return {Roo.TabPanelItem}
26872      */
26873     getTab : function(id){
26874         return this.items[id];
26875     },
26876
26877     /**
26878      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26879      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26880      */
26881     hideTab : function(id){
26882         var t = this.items[id];
26883         if(!t.isHidden()){
26884            t.setHidden(true);
26885            this.hiddenCount++;
26886            this.autoSizeTabs();
26887         }
26888     },
26889
26890     /**
26891      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26892      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26893      */
26894     unhideTab : function(id){
26895         var t = this.items[id];
26896         if(t.isHidden()){
26897            t.setHidden(false);
26898            this.hiddenCount--;
26899            this.autoSizeTabs();
26900         }
26901     },
26902
26903     /**
26904      * Adds an existing {@link Roo.TabPanelItem}.
26905      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26906      */
26907     addTabItem : function(item){
26908         this.items[item.id] = item;
26909         this.items.push(item);
26910         if(this.resizeTabs){
26911            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26912            this.autoSizeTabs();
26913         }else{
26914             item.autoSize();
26915         }
26916     },
26917
26918     /**
26919      * Removes a {@link Roo.TabPanelItem}.
26920      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26921      */
26922     removeTab : function(id){
26923         var items = this.items;
26924         var tab = items[id];
26925         if(!tab) { return; }
26926         var index = items.indexOf(tab);
26927         if(this.active == tab && items.length > 1){
26928             var newTab = this.getNextAvailable(index);
26929             if(newTab) {
26930                 newTab.activate();
26931             }
26932         }
26933         this.stripEl.dom.removeChild(tab.pnode.dom);
26934         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26935             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26936         }
26937         items.splice(index, 1);
26938         delete this.items[tab.id];
26939         tab.fireEvent("close", tab);
26940         tab.purgeListeners();
26941         this.autoSizeTabs();
26942     },
26943
26944     getNextAvailable : function(start){
26945         var items = this.items;
26946         var index = start;
26947         // look for a next tab that will slide over to
26948         // replace the one being removed
26949         while(index < items.length){
26950             var item = items[++index];
26951             if(item && !item.isHidden()){
26952                 return item;
26953             }
26954         }
26955         // if one isn't found select the previous tab (on the left)
26956         index = start;
26957         while(index >= 0){
26958             var item = items[--index];
26959             if(item && !item.isHidden()){
26960                 return item;
26961             }
26962         }
26963         return null;
26964     },
26965
26966     /**
26967      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26968      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26969      */
26970     disableTab : function(id){
26971         var tab = this.items[id];
26972         if(tab && this.active != tab){
26973             tab.disable();
26974         }
26975     },
26976
26977     /**
26978      * Enables a {@link Roo.TabPanelItem} that is disabled.
26979      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26980      */
26981     enableTab : function(id){
26982         var tab = this.items[id];
26983         tab.enable();
26984     },
26985
26986     /**
26987      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26988      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26989      * @return {Roo.TabPanelItem} The TabPanelItem.
26990      */
26991     activate : function(id){
26992         var tab = this.items[id];
26993         if(!tab){
26994             return null;
26995         }
26996         if(tab == this.active || tab.disabled){
26997             return tab;
26998         }
26999         var e = {};
27000         this.fireEvent("beforetabchange", this, e, tab);
27001         if(e.cancel !== true && !tab.disabled){
27002             if(this.active){
27003                 this.active.hide();
27004             }
27005             this.active = this.items[id];
27006             this.active.show();
27007             this.fireEvent("tabchange", this, this.active);
27008         }
27009         return tab;
27010     },
27011
27012     /**
27013      * Gets the active {@link Roo.TabPanelItem}.
27014      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
27015      */
27016     getActiveTab : function(){
27017         return this.active;
27018     },
27019
27020     /**
27021      * Updates the tab body element to fit the height of the container element
27022      * for overflow scrolling
27023      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27024      */
27025     syncHeight : function(targetHeight){
27026         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27027         var bm = this.bodyEl.getMargins();
27028         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27029         this.bodyEl.setHeight(newHeight);
27030         return newHeight;
27031     },
27032
27033     onResize : function(){
27034         if(this.monitorResize){
27035             this.autoSizeTabs();
27036         }
27037     },
27038
27039     /**
27040      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27041      */
27042     beginUpdate : function(){
27043         this.updating = true;
27044     },
27045
27046     /**
27047      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27048      */
27049     endUpdate : function(){
27050         this.updating = false;
27051         this.autoSizeTabs();
27052     },
27053
27054     /**
27055      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27056      */
27057     autoSizeTabs : function(){
27058         var count = this.items.length;
27059         var vcount = count - this.hiddenCount;
27060         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
27061             return;
27062         }
27063         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27064         var availWidth = Math.floor(w / vcount);
27065         var b = this.stripBody;
27066         if(b.getWidth() > w){
27067             var tabs = this.items;
27068             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27069             if(availWidth < this.minTabWidth){
27070                 /*if(!this.sleft){    // incomplete scrolling code
27071                     this.createScrollButtons();
27072                 }
27073                 this.showScroll();
27074                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27075             }
27076         }else{
27077             if(this.currentTabWidth < this.preferredTabWidth){
27078                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27079             }
27080         }
27081     },
27082
27083     /**
27084      * Returns the number of tabs in this TabPanel.
27085      * @return {Number}
27086      */
27087      getCount : function(){
27088          return this.items.length;
27089      },
27090
27091     /**
27092      * Resizes all the tabs to the passed width
27093      * @param {Number} The new width
27094      */
27095     setTabWidth : function(width){
27096         this.currentTabWidth = width;
27097         for(var i = 0, len = this.items.length; i < len; i++) {
27098                 if(!this.items[i].isHidden()) {
27099                 this.items[i].setWidth(width);
27100             }
27101         }
27102     },
27103
27104     /**
27105      * Destroys this TabPanel
27106      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27107      */
27108     destroy : function(removeEl){
27109         Roo.EventManager.removeResizeListener(this.onResize, this);
27110         for(var i = 0, len = this.items.length; i < len; i++){
27111             this.items[i].purgeListeners();
27112         }
27113         if(removeEl === true){
27114             this.el.update("");
27115             this.el.remove();
27116         }
27117     }
27118 });
27119
27120 /**
27121  * @class Roo.TabPanelItem
27122  * @extends Roo.util.Observable
27123  * Represents an individual item (tab plus body) in a TabPanel.
27124  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27125  * @param {String} id The id of this TabPanelItem
27126  * @param {String} text The text for the tab of this TabPanelItem
27127  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27128  */
27129 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27130     /**
27131      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27132      * @type Roo.TabPanel
27133      */
27134     this.tabPanel = tabPanel;
27135     /**
27136      * The id for this TabPanelItem
27137      * @type String
27138      */
27139     this.id = id;
27140     /** @private */
27141     this.disabled = false;
27142     /** @private */
27143     this.text = text;
27144     /** @private */
27145     this.loaded = false;
27146     this.closable = closable;
27147
27148     /**
27149      * The body element for this TabPanelItem.
27150      * @type Roo.Element
27151      */
27152     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27153     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27154     this.bodyEl.setStyle("display", "block");
27155     this.bodyEl.setStyle("zoom", "1");
27156     this.hideAction();
27157
27158     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27159     /** @private */
27160     this.el = Roo.get(els.el, true);
27161     this.inner = Roo.get(els.inner, true);
27162     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27163     this.pnode = Roo.get(els.el.parentNode, true);
27164     this.el.on("mousedown", this.onTabMouseDown, this);
27165     this.el.on("click", this.onTabClick, this);
27166     /** @private */
27167     if(closable){
27168         var c = Roo.get(els.close, true);
27169         c.dom.title = this.closeText;
27170         c.addClassOnOver("close-over");
27171         c.on("click", this.closeClick, this);
27172      }
27173
27174     this.addEvents({
27175          /**
27176          * @event activate
27177          * Fires when this tab becomes the active tab.
27178          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27179          * @param {Roo.TabPanelItem} this
27180          */
27181         "activate": true,
27182         /**
27183          * @event beforeclose
27184          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27185          * @param {Roo.TabPanelItem} this
27186          * @param {Object} e Set cancel to true on this object to cancel the close.
27187          */
27188         "beforeclose": true,
27189         /**
27190          * @event close
27191          * Fires when this tab is closed.
27192          * @param {Roo.TabPanelItem} this
27193          */
27194          "close": true,
27195         /**
27196          * @event deactivate
27197          * Fires when this tab is no longer the active tab.
27198          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27199          * @param {Roo.TabPanelItem} this
27200          */
27201          "deactivate" : true
27202     });
27203     this.hidden = false;
27204
27205     Roo.TabPanelItem.superclass.constructor.call(this);
27206 };
27207
27208 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27209     purgeListeners : function(){
27210        Roo.util.Observable.prototype.purgeListeners.call(this);
27211        this.el.removeAllListeners();
27212     },
27213     /**
27214      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27215      */
27216     show : function(){
27217         this.pnode.addClass("on");
27218         this.showAction();
27219         if(Roo.isOpera){
27220             this.tabPanel.stripWrap.repaint();
27221         }
27222         this.fireEvent("activate", this.tabPanel, this);
27223     },
27224
27225     /**
27226      * Returns true if this tab is the active tab.
27227      * @return {Boolean}
27228      */
27229     isActive : function(){
27230         return this.tabPanel.getActiveTab() == this;
27231     },
27232
27233     /**
27234      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27235      */
27236     hide : function(){
27237         this.pnode.removeClass("on");
27238         this.hideAction();
27239         this.fireEvent("deactivate", this.tabPanel, this);
27240     },
27241
27242     hideAction : function(){
27243         this.bodyEl.hide();
27244         this.bodyEl.setStyle("position", "absolute");
27245         this.bodyEl.setLeft("-20000px");
27246         this.bodyEl.setTop("-20000px");
27247     },
27248
27249     showAction : function(){
27250         this.bodyEl.setStyle("position", "relative");
27251         this.bodyEl.setTop("");
27252         this.bodyEl.setLeft("");
27253         this.bodyEl.show();
27254     },
27255
27256     /**
27257      * Set the tooltip for the tab.
27258      * @param {String} tooltip The tab's tooltip
27259      */
27260     setTooltip : function(text){
27261         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27262             this.textEl.dom.qtip = text;
27263             this.textEl.dom.removeAttribute('title');
27264         }else{
27265             this.textEl.dom.title = text;
27266         }
27267     },
27268
27269     onTabClick : function(e){
27270         e.preventDefault();
27271         this.tabPanel.activate(this.id);
27272     },
27273
27274     onTabMouseDown : function(e){
27275         e.preventDefault();
27276         this.tabPanel.activate(this.id);
27277     },
27278
27279     getWidth : function(){
27280         return this.inner.getWidth();
27281     },
27282
27283     setWidth : function(width){
27284         var iwidth = width - this.pnode.getPadding("lr");
27285         this.inner.setWidth(iwidth);
27286         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27287         this.pnode.setWidth(width);
27288     },
27289
27290     /**
27291      * Show or hide the tab
27292      * @param {Boolean} hidden True to hide or false to show.
27293      */
27294     setHidden : function(hidden){
27295         this.hidden = hidden;
27296         this.pnode.setStyle("display", hidden ? "none" : "");
27297     },
27298
27299     /**
27300      * Returns true if this tab is "hidden"
27301      * @return {Boolean}
27302      */
27303     isHidden : function(){
27304         return this.hidden;
27305     },
27306
27307     /**
27308      * Returns the text for this tab
27309      * @return {String}
27310      */
27311     getText : function(){
27312         return this.text;
27313     },
27314
27315     autoSize : function(){
27316         //this.el.beginMeasure();
27317         this.textEl.setWidth(1);
27318         /*
27319          *  #2804 [new] Tabs in Roojs
27320          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27321          */
27322         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27323         //this.el.endMeasure();
27324     },
27325
27326     /**
27327      * Sets the text for the tab (Note: this also sets the tooltip text)
27328      * @param {String} text The tab's text and tooltip
27329      */
27330     setText : function(text){
27331         this.text = text;
27332         this.textEl.update(text);
27333         this.setTooltip(text);
27334         if(!this.tabPanel.resizeTabs){
27335             this.autoSize();
27336         }
27337     },
27338     /**
27339      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27340      */
27341     activate : function(){
27342         this.tabPanel.activate(this.id);
27343     },
27344
27345     /**
27346      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27347      */
27348     disable : function(){
27349         if(this.tabPanel.active != this){
27350             this.disabled = true;
27351             this.pnode.addClass("disabled");
27352         }
27353     },
27354
27355     /**
27356      * Enables this TabPanelItem if it was previously disabled.
27357      */
27358     enable : function(){
27359         this.disabled = false;
27360         this.pnode.removeClass("disabled");
27361     },
27362
27363     /**
27364      * Sets the content for this TabPanelItem.
27365      * @param {String} content The content
27366      * @param {Boolean} loadScripts true to look for and load scripts
27367      */
27368     setContent : function(content, loadScripts){
27369         this.bodyEl.update(content, loadScripts);
27370     },
27371
27372     /**
27373      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27374      * @return {Roo.UpdateManager} The UpdateManager
27375      */
27376     getUpdateManager : function(){
27377         return this.bodyEl.getUpdateManager();
27378     },
27379
27380     /**
27381      * Set a URL to be used to load the content for this TabPanelItem.
27382      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27383      * @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)
27384      * @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)
27385      * @return {Roo.UpdateManager} The UpdateManager
27386      */
27387     setUrl : function(url, params, loadOnce){
27388         if(this.refreshDelegate){
27389             this.un('activate', this.refreshDelegate);
27390         }
27391         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27392         this.on("activate", this.refreshDelegate);
27393         return this.bodyEl.getUpdateManager();
27394     },
27395
27396     /** @private */
27397     _handleRefresh : function(url, params, loadOnce){
27398         if(!loadOnce || !this.loaded){
27399             var updater = this.bodyEl.getUpdateManager();
27400             updater.update(url, params, this._setLoaded.createDelegate(this));
27401         }
27402     },
27403
27404     /**
27405      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27406      *   Will fail silently if the setUrl method has not been called.
27407      *   This does not activate the panel, just updates its content.
27408      */
27409     refresh : function(){
27410         if(this.refreshDelegate){
27411            this.loaded = false;
27412            this.refreshDelegate();
27413         }
27414     },
27415
27416     /** @private */
27417     _setLoaded : function(){
27418         this.loaded = true;
27419     },
27420
27421     /** @private */
27422     closeClick : function(e){
27423         var o = {};
27424         e.stopEvent();
27425         this.fireEvent("beforeclose", this, o);
27426         if(o.cancel !== true){
27427             this.tabPanel.removeTab(this.id);
27428         }
27429     },
27430     /**
27431      * The text displayed in the tooltip for the close icon.
27432      * @type String
27433      */
27434     closeText : "Close this tab"
27435 });
27436
27437 /** @private */
27438 Roo.TabPanel.prototype.createStrip = function(container){
27439     var strip = document.createElement("div");
27440     strip.className = "x-tabs-wrap";
27441     container.appendChild(strip);
27442     return strip;
27443 };
27444 /** @private */
27445 Roo.TabPanel.prototype.createStripList = function(strip){
27446     // div wrapper for retard IE
27447     // returns the "tr" element.
27448     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27449         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27450         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27451     return strip.firstChild.firstChild.firstChild.firstChild;
27452 };
27453 /** @private */
27454 Roo.TabPanel.prototype.createBody = function(container){
27455     var body = document.createElement("div");
27456     Roo.id(body, "tab-body");
27457     Roo.fly(body).addClass("x-tabs-body");
27458     container.appendChild(body);
27459     return body;
27460 };
27461 /** @private */
27462 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27463     var body = Roo.getDom(id);
27464     if(!body){
27465         body = document.createElement("div");
27466         body.id = id;
27467     }
27468     Roo.fly(body).addClass("x-tabs-item-body");
27469     bodyEl.insertBefore(body, bodyEl.firstChild);
27470     return body;
27471 };
27472 /** @private */
27473 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27474     var td = document.createElement("td");
27475     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27476     //stripEl.appendChild(td);
27477     if(closable){
27478         td.className = "x-tabs-closable";
27479         if(!this.closeTpl){
27480             this.closeTpl = new Roo.Template(
27481                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27482                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27483                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27484             );
27485         }
27486         var el = this.closeTpl.overwrite(td, {"text": text});
27487         var close = el.getElementsByTagName("div")[0];
27488         var inner = el.getElementsByTagName("em")[0];
27489         return {"el": el, "close": close, "inner": inner};
27490     } else {
27491         if(!this.tabTpl){
27492             this.tabTpl = new Roo.Template(
27493                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27494                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27495             );
27496         }
27497         var el = this.tabTpl.overwrite(td, {"text": text});
27498         var inner = el.getElementsByTagName("em")[0];
27499         return {"el": el, "inner": inner};
27500     }
27501 };/*
27502  * Based on:
27503  * Ext JS Library 1.1.1
27504  * Copyright(c) 2006-2007, Ext JS, LLC.
27505  *
27506  * Originally Released Under LGPL - original licence link has changed is not relivant.
27507  *
27508  * Fork - LGPL
27509  * <script type="text/javascript">
27510  */
27511
27512 /**
27513  * @class Roo.Button
27514  * @extends Roo.util.Observable
27515  * Simple Button class
27516  * @cfg {String} text The button text
27517  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27518  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27519  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27520  * @cfg {Object} scope The scope of the handler
27521  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27522  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27523  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27524  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27525  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27526  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27527    applies if enableToggle = true)
27528  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27529  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27530   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27531  * @constructor
27532  * Create a new button
27533  * @param {Object} config The config object
27534  */
27535 Roo.Button = function(renderTo, config)
27536 {
27537     if (!config) {
27538         config = renderTo;
27539         renderTo = config.renderTo || false;
27540     }
27541     
27542     Roo.apply(this, config);
27543     this.addEvents({
27544         /**
27545              * @event click
27546              * Fires when this button is clicked
27547              * @param {Button} this
27548              * @param {EventObject} e The click event
27549              */
27550             "click" : true,
27551         /**
27552              * @event toggle
27553              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27554              * @param {Button} this
27555              * @param {Boolean} pressed
27556              */
27557             "toggle" : true,
27558         /**
27559              * @event mouseover
27560              * Fires when the mouse hovers over the button
27561              * @param {Button} this
27562              * @param {Event} e The event object
27563              */
27564         'mouseover' : true,
27565         /**
27566              * @event mouseout
27567              * Fires when the mouse exits the button
27568              * @param {Button} this
27569              * @param {Event} e The event object
27570              */
27571         'mouseout': true,
27572          /**
27573              * @event render
27574              * Fires when the button is rendered
27575              * @param {Button} this
27576              */
27577         'render': true
27578     });
27579     if(this.menu){
27580         this.menu = Roo.menu.MenuMgr.get(this.menu);
27581     }
27582     // register listeners first!!  - so render can be captured..
27583     Roo.util.Observable.call(this);
27584     if(renderTo){
27585         this.render(renderTo);
27586     }
27587     
27588   
27589 };
27590
27591 Roo.extend(Roo.Button, Roo.util.Observable, {
27592     /**
27593      * 
27594      */
27595     
27596     /**
27597      * Read-only. True if this button is hidden
27598      * @type Boolean
27599      */
27600     hidden : false,
27601     /**
27602      * Read-only. True if this button is disabled
27603      * @type Boolean
27604      */
27605     disabled : false,
27606     /**
27607      * Read-only. True if this button is pressed (only if enableToggle = true)
27608      * @type Boolean
27609      */
27610     pressed : false,
27611
27612     /**
27613      * @cfg {Number} tabIndex 
27614      * The DOM tabIndex for this button (defaults to undefined)
27615      */
27616     tabIndex : undefined,
27617
27618     /**
27619      * @cfg {Boolean} enableToggle
27620      * True to enable pressed/not pressed toggling (defaults to false)
27621      */
27622     enableToggle: false,
27623     /**
27624      * @cfg {Mixed} menu
27625      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27626      */
27627     menu : undefined,
27628     /**
27629      * @cfg {String} menuAlign
27630      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27631      */
27632     menuAlign : "tl-bl?",
27633
27634     /**
27635      * @cfg {String} iconCls
27636      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27637      */
27638     iconCls : undefined,
27639     /**
27640      * @cfg {String} type
27641      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27642      */
27643     type : 'button',
27644
27645     // private
27646     menuClassTarget: 'tr',
27647
27648     /**
27649      * @cfg {String} clickEvent
27650      * The type of event to map to the button's event handler (defaults to 'click')
27651      */
27652     clickEvent : 'click',
27653
27654     /**
27655      * @cfg {Boolean} handleMouseEvents
27656      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27657      */
27658     handleMouseEvents : true,
27659
27660     /**
27661      * @cfg {String} tooltipType
27662      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27663      */
27664     tooltipType : 'qtip',
27665
27666     /**
27667      * @cfg {String} cls
27668      * A CSS class to apply to the button's main element.
27669      */
27670     
27671     /**
27672      * @cfg {Roo.Template} template (Optional)
27673      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27674      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27675      * require code modifications if required elements (e.g. a button) aren't present.
27676      */
27677
27678     // private
27679     render : function(renderTo){
27680         var btn;
27681         if(this.hideParent){
27682             this.parentEl = Roo.get(renderTo);
27683         }
27684         if(!this.dhconfig){
27685             if(!this.template){
27686                 if(!Roo.Button.buttonTemplate){
27687                     // hideous table template
27688                     Roo.Button.buttonTemplate = new Roo.Template(
27689                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27690                         '<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>',
27691                         "</tr></tbody></table>");
27692                 }
27693                 this.template = Roo.Button.buttonTemplate;
27694             }
27695             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27696             var btnEl = btn.child("button:first");
27697             btnEl.on('focus', this.onFocus, this);
27698             btnEl.on('blur', this.onBlur, this);
27699             if(this.cls){
27700                 btn.addClass(this.cls);
27701             }
27702             if(this.icon){
27703                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27704             }
27705             if(this.iconCls){
27706                 btnEl.addClass(this.iconCls);
27707                 if(!this.cls){
27708                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27709                 }
27710             }
27711             if(this.tabIndex !== undefined){
27712                 btnEl.dom.tabIndex = this.tabIndex;
27713             }
27714             if(this.tooltip){
27715                 if(typeof this.tooltip == 'object'){
27716                     Roo.QuickTips.tips(Roo.apply({
27717                           target: btnEl.id
27718                     }, this.tooltip));
27719                 } else {
27720                     btnEl.dom[this.tooltipType] = this.tooltip;
27721                 }
27722             }
27723         }else{
27724             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27725         }
27726         this.el = btn;
27727         if(this.id){
27728             this.el.dom.id = this.el.id = this.id;
27729         }
27730         if(this.menu){
27731             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27732             this.menu.on("show", this.onMenuShow, this);
27733             this.menu.on("hide", this.onMenuHide, this);
27734         }
27735         btn.addClass("x-btn");
27736         if(Roo.isIE && !Roo.isIE7){
27737             this.autoWidth.defer(1, this);
27738         }else{
27739             this.autoWidth();
27740         }
27741         if(this.handleMouseEvents){
27742             btn.on("mouseover", this.onMouseOver, this);
27743             btn.on("mouseout", this.onMouseOut, this);
27744             btn.on("mousedown", this.onMouseDown, this);
27745         }
27746         btn.on(this.clickEvent, this.onClick, this);
27747         //btn.on("mouseup", this.onMouseUp, this);
27748         if(this.hidden){
27749             this.hide();
27750         }
27751         if(this.disabled){
27752             this.disable();
27753         }
27754         Roo.ButtonToggleMgr.register(this);
27755         if(this.pressed){
27756             this.el.addClass("x-btn-pressed");
27757         }
27758         if(this.repeat){
27759             var repeater = new Roo.util.ClickRepeater(btn,
27760                 typeof this.repeat == "object" ? this.repeat : {}
27761             );
27762             repeater.on("click", this.onClick,  this);
27763         }
27764         
27765         this.fireEvent('render', this);
27766         
27767     },
27768     /**
27769      * Returns the button's underlying element
27770      * @return {Roo.Element} The element
27771      */
27772     getEl : function(){
27773         return this.el;  
27774     },
27775     
27776     /**
27777      * Destroys this Button and removes any listeners.
27778      */
27779     destroy : function(){
27780         Roo.ButtonToggleMgr.unregister(this);
27781         this.el.removeAllListeners();
27782         this.purgeListeners();
27783         this.el.remove();
27784     },
27785
27786     // private
27787     autoWidth : function(){
27788         if(this.el){
27789             this.el.setWidth("auto");
27790             if(Roo.isIE7 && Roo.isStrict){
27791                 var ib = this.el.child('button');
27792                 if(ib && ib.getWidth() > 20){
27793                     ib.clip();
27794                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27795                 }
27796             }
27797             if(this.minWidth){
27798                 if(this.hidden){
27799                     this.el.beginMeasure();
27800                 }
27801                 if(this.el.getWidth() < this.minWidth){
27802                     this.el.setWidth(this.minWidth);
27803                 }
27804                 if(this.hidden){
27805                     this.el.endMeasure();
27806                 }
27807             }
27808         }
27809     },
27810
27811     /**
27812      * Assigns this button's click handler
27813      * @param {Function} handler The function to call when the button is clicked
27814      * @param {Object} scope (optional) Scope for the function passed in
27815      */
27816     setHandler : function(handler, scope){
27817         this.handler = handler;
27818         this.scope = scope;  
27819     },
27820     
27821     /**
27822      * Sets this button's text
27823      * @param {String} text The button text
27824      */
27825     setText : function(text){
27826         this.text = text;
27827         if(this.el){
27828             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27829         }
27830         this.autoWidth();
27831     },
27832     
27833     /**
27834      * Gets the text for this button
27835      * @return {String} The button text
27836      */
27837     getText : function(){
27838         return this.text;  
27839     },
27840     
27841     /**
27842      * Show this button
27843      */
27844     show: function(){
27845         this.hidden = false;
27846         if(this.el){
27847             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27848         }
27849     },
27850     
27851     /**
27852      * Hide this button
27853      */
27854     hide: function(){
27855         this.hidden = true;
27856         if(this.el){
27857             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27858         }
27859     },
27860     
27861     /**
27862      * Convenience function for boolean show/hide
27863      * @param {Boolean} visible True to show, false to hide
27864      */
27865     setVisible: function(visible){
27866         if(visible) {
27867             this.show();
27868         }else{
27869             this.hide();
27870         }
27871     },
27872     
27873     /**
27874      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27875      * @param {Boolean} state (optional) Force a particular state
27876      */
27877     toggle : function(state){
27878         state = state === undefined ? !this.pressed : state;
27879         if(state != this.pressed){
27880             if(state){
27881                 this.el.addClass("x-btn-pressed");
27882                 this.pressed = true;
27883                 this.fireEvent("toggle", this, true);
27884             }else{
27885                 this.el.removeClass("x-btn-pressed");
27886                 this.pressed = false;
27887                 this.fireEvent("toggle", this, false);
27888             }
27889             if(this.toggleHandler){
27890                 this.toggleHandler.call(this.scope || this, this, state);
27891             }
27892         }
27893     },
27894     
27895     /**
27896      * Focus the button
27897      */
27898     focus : function(){
27899         this.el.child('button:first').focus();
27900     },
27901     
27902     /**
27903      * Disable this button
27904      */
27905     disable : function(){
27906         if(this.el){
27907             this.el.addClass("x-btn-disabled");
27908         }
27909         this.disabled = true;
27910     },
27911     
27912     /**
27913      * Enable this button
27914      */
27915     enable : function(){
27916         if(this.el){
27917             this.el.removeClass("x-btn-disabled");
27918         }
27919         this.disabled = false;
27920     },
27921
27922     /**
27923      * Convenience function for boolean enable/disable
27924      * @param {Boolean} enabled True to enable, false to disable
27925      */
27926     setDisabled : function(v){
27927         this[v !== true ? "enable" : "disable"]();
27928     },
27929
27930     // private
27931     onClick : function(e)
27932     {
27933         if(e){
27934             e.preventDefault();
27935         }
27936         if(e.button != 0){
27937             return;
27938         }
27939         if(!this.disabled){
27940             if(this.enableToggle){
27941                 this.toggle();
27942             }
27943             if(this.menu && !this.menu.isVisible()){
27944                 this.menu.show(this.el, this.menuAlign);
27945             }
27946             this.fireEvent("click", this, e);
27947             if(this.handler){
27948                 this.el.removeClass("x-btn-over");
27949                 this.handler.call(this.scope || this, this, e);
27950             }
27951         }
27952     },
27953     // private
27954     onMouseOver : function(e){
27955         if(!this.disabled){
27956             this.el.addClass("x-btn-over");
27957             this.fireEvent('mouseover', this, e);
27958         }
27959     },
27960     // private
27961     onMouseOut : function(e){
27962         if(!e.within(this.el,  true)){
27963             this.el.removeClass("x-btn-over");
27964             this.fireEvent('mouseout', this, e);
27965         }
27966     },
27967     // private
27968     onFocus : function(e){
27969         if(!this.disabled){
27970             this.el.addClass("x-btn-focus");
27971         }
27972     },
27973     // private
27974     onBlur : function(e){
27975         this.el.removeClass("x-btn-focus");
27976     },
27977     // private
27978     onMouseDown : function(e){
27979         if(!this.disabled && e.button == 0){
27980             this.el.addClass("x-btn-click");
27981             Roo.get(document).on('mouseup', this.onMouseUp, this);
27982         }
27983     },
27984     // private
27985     onMouseUp : function(e){
27986         if(e.button == 0){
27987             this.el.removeClass("x-btn-click");
27988             Roo.get(document).un('mouseup', this.onMouseUp, this);
27989         }
27990     },
27991     // private
27992     onMenuShow : function(e){
27993         this.el.addClass("x-btn-menu-active");
27994     },
27995     // private
27996     onMenuHide : function(e){
27997         this.el.removeClass("x-btn-menu-active");
27998     }   
27999 });
28000
28001 // Private utility class used by Button
28002 Roo.ButtonToggleMgr = function(){
28003    var groups = {};
28004    
28005    function toggleGroup(btn, state){
28006        if(state){
28007            var g = groups[btn.toggleGroup];
28008            for(var i = 0, l = g.length; i < l; i++){
28009                if(g[i] != btn){
28010                    g[i].toggle(false);
28011                }
28012            }
28013        }
28014    }
28015    
28016    return {
28017        register : function(btn){
28018            if(!btn.toggleGroup){
28019                return;
28020            }
28021            var g = groups[btn.toggleGroup];
28022            if(!g){
28023                g = groups[btn.toggleGroup] = [];
28024            }
28025            g.push(btn);
28026            btn.on("toggle", toggleGroup);
28027        },
28028        
28029        unregister : function(btn){
28030            if(!btn.toggleGroup){
28031                return;
28032            }
28033            var g = groups[btn.toggleGroup];
28034            if(g){
28035                g.remove(btn);
28036                btn.un("toggle", toggleGroup);
28037            }
28038        }
28039    };
28040 }();/*
28041  * Based on:
28042  * Ext JS Library 1.1.1
28043  * Copyright(c) 2006-2007, Ext JS, LLC.
28044  *
28045  * Originally Released Under LGPL - original licence link has changed is not relivant.
28046  *
28047  * Fork - LGPL
28048  * <script type="text/javascript">
28049  */
28050  
28051 /**
28052  * @class Roo.SplitButton
28053  * @extends Roo.Button
28054  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28055  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28056  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28057  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28058  * @cfg {String} arrowTooltip The title attribute of the arrow
28059  * @constructor
28060  * Create a new menu button
28061  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28062  * @param {Object} config The config object
28063  */
28064 Roo.SplitButton = function(renderTo, config){
28065     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28066     /**
28067      * @event arrowclick
28068      * Fires when this button's arrow is clicked
28069      * @param {SplitButton} this
28070      * @param {EventObject} e The click event
28071      */
28072     this.addEvents({"arrowclick":true});
28073 };
28074
28075 Roo.extend(Roo.SplitButton, Roo.Button, {
28076     render : function(renderTo){
28077         // this is one sweet looking template!
28078         var tpl = new Roo.Template(
28079             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28080             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28081             '<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>',
28082             "</tbody></table></td><td>",
28083             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28084             '<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>',
28085             "</tbody></table></td></tr></table>"
28086         );
28087         var btn = tpl.append(renderTo, [this.text, this.type], true);
28088         var btnEl = btn.child("button");
28089         if(this.cls){
28090             btn.addClass(this.cls);
28091         }
28092         if(this.icon){
28093             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28094         }
28095         if(this.iconCls){
28096             btnEl.addClass(this.iconCls);
28097             if(!this.cls){
28098                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28099             }
28100         }
28101         this.el = btn;
28102         if(this.handleMouseEvents){
28103             btn.on("mouseover", this.onMouseOver, this);
28104             btn.on("mouseout", this.onMouseOut, this);
28105             btn.on("mousedown", this.onMouseDown, this);
28106             btn.on("mouseup", this.onMouseUp, this);
28107         }
28108         btn.on(this.clickEvent, this.onClick, this);
28109         if(this.tooltip){
28110             if(typeof this.tooltip == 'object'){
28111                 Roo.QuickTips.tips(Roo.apply({
28112                       target: btnEl.id
28113                 }, this.tooltip));
28114             } else {
28115                 btnEl.dom[this.tooltipType] = this.tooltip;
28116             }
28117         }
28118         if(this.arrowTooltip){
28119             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28120         }
28121         if(this.hidden){
28122             this.hide();
28123         }
28124         if(this.disabled){
28125             this.disable();
28126         }
28127         if(this.pressed){
28128             this.el.addClass("x-btn-pressed");
28129         }
28130         if(Roo.isIE && !Roo.isIE7){
28131             this.autoWidth.defer(1, this);
28132         }else{
28133             this.autoWidth();
28134         }
28135         if(this.menu){
28136             this.menu.on("show", this.onMenuShow, this);
28137             this.menu.on("hide", this.onMenuHide, this);
28138         }
28139         this.fireEvent('render', this);
28140     },
28141
28142     // private
28143     autoWidth : function(){
28144         if(this.el){
28145             var tbl = this.el.child("table:first");
28146             var tbl2 = this.el.child("table:last");
28147             this.el.setWidth("auto");
28148             tbl.setWidth("auto");
28149             if(Roo.isIE7 && Roo.isStrict){
28150                 var ib = this.el.child('button:first');
28151                 if(ib && ib.getWidth() > 20){
28152                     ib.clip();
28153                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28154                 }
28155             }
28156             if(this.minWidth){
28157                 if(this.hidden){
28158                     this.el.beginMeasure();
28159                 }
28160                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28161                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28162                 }
28163                 if(this.hidden){
28164                     this.el.endMeasure();
28165                 }
28166             }
28167             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28168         } 
28169     },
28170     /**
28171      * Sets this button's click handler
28172      * @param {Function} handler The function to call when the button is clicked
28173      * @param {Object} scope (optional) Scope for the function passed above
28174      */
28175     setHandler : function(handler, scope){
28176         this.handler = handler;
28177         this.scope = scope;  
28178     },
28179     
28180     /**
28181      * Sets this button's arrow click handler
28182      * @param {Function} handler The function to call when the arrow is clicked
28183      * @param {Object} scope (optional) Scope for the function passed above
28184      */
28185     setArrowHandler : function(handler, scope){
28186         this.arrowHandler = handler;
28187         this.scope = scope;  
28188     },
28189     
28190     /**
28191      * Focus the button
28192      */
28193     focus : function(){
28194         if(this.el){
28195             this.el.child("button:first").focus();
28196         }
28197     },
28198
28199     // private
28200     onClick : function(e){
28201         e.preventDefault();
28202         if(!this.disabled){
28203             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28204                 if(this.menu && !this.menu.isVisible()){
28205                     this.menu.show(this.el, this.menuAlign);
28206                 }
28207                 this.fireEvent("arrowclick", this, e);
28208                 if(this.arrowHandler){
28209                     this.arrowHandler.call(this.scope || this, this, e);
28210                 }
28211             }else{
28212                 this.fireEvent("click", this, e);
28213                 if(this.handler){
28214                     this.handler.call(this.scope || this, this, e);
28215                 }
28216             }
28217         }
28218     },
28219     // private
28220     onMouseDown : function(e){
28221         if(!this.disabled){
28222             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28223         }
28224     },
28225     // private
28226     onMouseUp : function(e){
28227         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28228     }   
28229 });
28230
28231
28232 // backwards compat
28233 Roo.MenuButton = Roo.SplitButton;/*
28234  * Based on:
28235  * Ext JS Library 1.1.1
28236  * Copyright(c) 2006-2007, Ext JS, LLC.
28237  *
28238  * Originally Released Under LGPL - original licence link has changed is not relivant.
28239  *
28240  * Fork - LGPL
28241  * <script type="text/javascript">
28242  */
28243
28244 /**
28245  * @class Roo.Toolbar
28246  * Basic Toolbar class.
28247  * @constructor
28248  * Creates a new Toolbar
28249  * @param {Object} container The config object
28250  */ 
28251 Roo.Toolbar = function(container, buttons, config)
28252 {
28253     /// old consturctor format still supported..
28254     if(container instanceof Array){ // omit the container for later rendering
28255         buttons = container;
28256         config = buttons;
28257         container = null;
28258     }
28259     if (typeof(container) == 'object' && container.xtype) {
28260         config = container;
28261         container = config.container;
28262         buttons = config.buttons || []; // not really - use items!!
28263     }
28264     var xitems = [];
28265     if (config && config.items) {
28266         xitems = config.items;
28267         delete config.items;
28268     }
28269     Roo.apply(this, config);
28270     this.buttons = buttons;
28271     
28272     if(container){
28273         this.render(container);
28274     }
28275     this.xitems = xitems;
28276     Roo.each(xitems, function(b) {
28277         this.add(b);
28278     }, this);
28279     
28280 };
28281
28282 Roo.Toolbar.prototype = {
28283     /**
28284      * @cfg {Array} items
28285      * array of button configs or elements to add (will be converted to a MixedCollection)
28286      */
28287     
28288     /**
28289      * @cfg {String/HTMLElement/Element} container
28290      * The id or element that will contain the toolbar
28291      */
28292     // private
28293     render : function(ct){
28294         this.el = Roo.get(ct);
28295         if(this.cls){
28296             this.el.addClass(this.cls);
28297         }
28298         // using a table allows for vertical alignment
28299         // 100% width is needed by Safari...
28300         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28301         this.tr = this.el.child("tr", true);
28302         var autoId = 0;
28303         this.items = new Roo.util.MixedCollection(false, function(o){
28304             return o.id || ("item" + (++autoId));
28305         });
28306         if(this.buttons){
28307             this.add.apply(this, this.buttons);
28308             delete this.buttons;
28309         }
28310     },
28311
28312     /**
28313      * Adds element(s) to the toolbar -- this function takes a variable number of 
28314      * arguments of mixed type and adds them to the toolbar.
28315      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28316      * <ul>
28317      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28318      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28319      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28320      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28321      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28322      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28323      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28324      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28325      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28326      * </ul>
28327      * @param {Mixed} arg2
28328      * @param {Mixed} etc.
28329      */
28330     add : function(){
28331         var a = arguments, l = a.length;
28332         for(var i = 0; i < l; i++){
28333             this._add(a[i]);
28334         }
28335     },
28336     // private..
28337     _add : function(el) {
28338         
28339         if (el.xtype) {
28340             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28341         }
28342         
28343         if (el.applyTo){ // some kind of form field
28344             return this.addField(el);
28345         } 
28346         if (el.render){ // some kind of Toolbar.Item
28347             return this.addItem(el);
28348         }
28349         if (typeof el == "string"){ // string
28350             if(el == "separator" || el == "-"){
28351                 return this.addSeparator();
28352             }
28353             if (el == " "){
28354                 return this.addSpacer();
28355             }
28356             if(el == "->"){
28357                 return this.addFill();
28358             }
28359             return this.addText(el);
28360             
28361         }
28362         if(el.tagName){ // element
28363             return this.addElement(el);
28364         }
28365         if(typeof el == "object"){ // must be button config?
28366             return this.addButton(el);
28367         }
28368         // and now what?!?!
28369         return false;
28370         
28371     },
28372     
28373     /**
28374      * Add an Xtype element
28375      * @param {Object} xtype Xtype Object
28376      * @return {Object} created Object
28377      */
28378     addxtype : function(e){
28379         return this.add(e);  
28380     },
28381     
28382     /**
28383      * Returns the Element for this toolbar.
28384      * @return {Roo.Element}
28385      */
28386     getEl : function(){
28387         return this.el;  
28388     },
28389     
28390     /**
28391      * Adds a separator
28392      * @return {Roo.Toolbar.Item} The separator item
28393      */
28394     addSeparator : function(){
28395         return this.addItem(new Roo.Toolbar.Separator());
28396     },
28397
28398     /**
28399      * Adds a spacer element
28400      * @return {Roo.Toolbar.Spacer} The spacer item
28401      */
28402     addSpacer : function(){
28403         return this.addItem(new Roo.Toolbar.Spacer());
28404     },
28405
28406     /**
28407      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28408      * @return {Roo.Toolbar.Fill} The fill item
28409      */
28410     addFill : function(){
28411         return this.addItem(new Roo.Toolbar.Fill());
28412     },
28413
28414     /**
28415      * Adds any standard HTML element to the toolbar
28416      * @param {String/HTMLElement/Element} el The element or id of the element to add
28417      * @return {Roo.Toolbar.Item} The element's item
28418      */
28419     addElement : function(el){
28420         return this.addItem(new Roo.Toolbar.Item(el));
28421     },
28422     /**
28423      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28424      * @type Roo.util.MixedCollection  
28425      */
28426     items : false,
28427      
28428     /**
28429      * Adds any Toolbar.Item or subclass
28430      * @param {Roo.Toolbar.Item} item
28431      * @return {Roo.Toolbar.Item} The item
28432      */
28433     addItem : function(item){
28434         var td = this.nextBlock();
28435         item.render(td);
28436         this.items.add(item);
28437         return item;
28438     },
28439     
28440     /**
28441      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28442      * @param {Object/Array} config A button config or array of configs
28443      * @return {Roo.Toolbar.Button/Array}
28444      */
28445     addButton : function(config){
28446         if(config instanceof Array){
28447             var buttons = [];
28448             for(var i = 0, len = config.length; i < len; i++) {
28449                 buttons.push(this.addButton(config[i]));
28450             }
28451             return buttons;
28452         }
28453         var b = config;
28454         if(!(config instanceof Roo.Toolbar.Button)){
28455             b = config.split ?
28456                 new Roo.Toolbar.SplitButton(config) :
28457                 new Roo.Toolbar.Button(config);
28458         }
28459         var td = this.nextBlock();
28460         b.render(td);
28461         this.items.add(b);
28462         return b;
28463     },
28464     
28465     /**
28466      * Adds text to the toolbar
28467      * @param {String} text The text to add
28468      * @return {Roo.Toolbar.Item} The element's item
28469      */
28470     addText : function(text){
28471         return this.addItem(new Roo.Toolbar.TextItem(text));
28472     },
28473     
28474     /**
28475      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28476      * @param {Number} index The index where the item is to be inserted
28477      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28478      * @return {Roo.Toolbar.Button/Item}
28479      */
28480     insertButton : function(index, item){
28481         if(item instanceof Array){
28482             var buttons = [];
28483             for(var i = 0, len = item.length; i < len; i++) {
28484                buttons.push(this.insertButton(index + i, item[i]));
28485             }
28486             return buttons;
28487         }
28488         if (!(item instanceof Roo.Toolbar.Button)){
28489            item = new Roo.Toolbar.Button(item);
28490         }
28491         var td = document.createElement("td");
28492         this.tr.insertBefore(td, this.tr.childNodes[index]);
28493         item.render(td);
28494         this.items.insert(index, item);
28495         return item;
28496     },
28497     
28498     /**
28499      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28500      * @param {Object} config
28501      * @return {Roo.Toolbar.Item} The element's item
28502      */
28503     addDom : function(config, returnEl){
28504         var td = this.nextBlock();
28505         Roo.DomHelper.overwrite(td, config);
28506         var ti = new Roo.Toolbar.Item(td.firstChild);
28507         ti.render(td);
28508         this.items.add(ti);
28509         return ti;
28510     },
28511
28512     /**
28513      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28514      * @type Roo.util.MixedCollection  
28515      */
28516     fields : false,
28517     
28518     /**
28519      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28520      * Note: the field should not have been rendered yet. For a field that has already been
28521      * rendered, use {@link #addElement}.
28522      * @param {Roo.form.Field} field
28523      * @return {Roo.ToolbarItem}
28524      */
28525      
28526       
28527     addField : function(field) {
28528         if (!this.fields) {
28529             var autoId = 0;
28530             this.fields = new Roo.util.MixedCollection(false, function(o){
28531                 return o.id || ("item" + (++autoId));
28532             });
28533
28534         }
28535         
28536         var td = this.nextBlock();
28537         field.render(td);
28538         var ti = new Roo.Toolbar.Item(td.firstChild);
28539         ti.render(td);
28540         this.items.add(ti);
28541         this.fields.add(field);
28542         return ti;
28543     },
28544     /**
28545      * Hide the toolbar
28546      * @method hide
28547      */
28548      
28549       
28550     hide : function()
28551     {
28552         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28553         this.el.child('div').hide();
28554     },
28555     /**
28556      * Show the toolbar
28557      * @method show
28558      */
28559     show : function()
28560     {
28561         this.el.child('div').show();
28562     },
28563       
28564     // private
28565     nextBlock : function(){
28566         var td = document.createElement("td");
28567         this.tr.appendChild(td);
28568         return td;
28569     },
28570
28571     // private
28572     destroy : function(){
28573         if(this.items){ // rendered?
28574             Roo.destroy.apply(Roo, this.items.items);
28575         }
28576         if(this.fields){ // rendered?
28577             Roo.destroy.apply(Roo, this.fields.items);
28578         }
28579         Roo.Element.uncache(this.el, this.tr);
28580     }
28581 };
28582
28583 /**
28584  * @class Roo.Toolbar.Item
28585  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28586  * @constructor
28587  * Creates a new Item
28588  * @param {HTMLElement} el 
28589  */
28590 Roo.Toolbar.Item = function(el){
28591     var cfg = {};
28592     if (typeof (el.xtype) != 'undefined') {
28593         cfg = el;
28594         el = cfg.el;
28595     }
28596     
28597     this.el = Roo.getDom(el);
28598     this.id = Roo.id(this.el);
28599     this.hidden = false;
28600     
28601     this.addEvents({
28602          /**
28603              * @event render
28604              * Fires when the button is rendered
28605              * @param {Button} this
28606              */
28607         'render': true
28608     });
28609     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28610 };
28611 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28612 //Roo.Toolbar.Item.prototype = {
28613     
28614     /**
28615      * Get this item's HTML Element
28616      * @return {HTMLElement}
28617      */
28618     getEl : function(){
28619        return this.el;  
28620     },
28621
28622     // private
28623     render : function(td){
28624         
28625          this.td = td;
28626         td.appendChild(this.el);
28627         
28628         this.fireEvent('render', this);
28629     },
28630     
28631     /**
28632      * Removes and destroys this item.
28633      */
28634     destroy : function(){
28635         this.td.parentNode.removeChild(this.td);
28636     },
28637     
28638     /**
28639      * Shows this item.
28640      */
28641     show: function(){
28642         this.hidden = false;
28643         this.td.style.display = "";
28644     },
28645     
28646     /**
28647      * Hides this item.
28648      */
28649     hide: function(){
28650         this.hidden = true;
28651         this.td.style.display = "none";
28652     },
28653     
28654     /**
28655      * Convenience function for boolean show/hide.
28656      * @param {Boolean} visible true to show/false to hide
28657      */
28658     setVisible: function(visible){
28659         if(visible) {
28660             this.show();
28661         }else{
28662             this.hide();
28663         }
28664     },
28665     
28666     /**
28667      * Try to focus this item.
28668      */
28669     focus : function(){
28670         Roo.fly(this.el).focus();
28671     },
28672     
28673     /**
28674      * Disables this item.
28675      */
28676     disable : function(){
28677         Roo.fly(this.td).addClass("x-item-disabled");
28678         this.disabled = true;
28679         this.el.disabled = true;
28680     },
28681     
28682     /**
28683      * Enables this item.
28684      */
28685     enable : function(){
28686         Roo.fly(this.td).removeClass("x-item-disabled");
28687         this.disabled = false;
28688         this.el.disabled = false;
28689     }
28690 });
28691
28692
28693 /**
28694  * @class Roo.Toolbar.Separator
28695  * @extends Roo.Toolbar.Item
28696  * A simple toolbar separator class
28697  * @constructor
28698  * Creates a new Separator
28699  */
28700 Roo.Toolbar.Separator = function(cfg){
28701     
28702     var s = document.createElement("span");
28703     s.className = "ytb-sep";
28704     if (cfg) {
28705         cfg.el = s;
28706     }
28707     
28708     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28709 };
28710 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28711     enable:Roo.emptyFn,
28712     disable:Roo.emptyFn,
28713     focus:Roo.emptyFn
28714 });
28715
28716 /**
28717  * @class Roo.Toolbar.Spacer
28718  * @extends Roo.Toolbar.Item
28719  * A simple element that adds extra horizontal space to a toolbar.
28720  * @constructor
28721  * Creates a new Spacer
28722  */
28723 Roo.Toolbar.Spacer = function(cfg){
28724     var s = document.createElement("div");
28725     s.className = "ytb-spacer";
28726     if (cfg) {
28727         cfg.el = s;
28728     }
28729     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28730 };
28731 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28732     enable:Roo.emptyFn,
28733     disable:Roo.emptyFn,
28734     focus:Roo.emptyFn
28735 });
28736
28737 /**
28738  * @class Roo.Toolbar.Fill
28739  * @extends Roo.Toolbar.Spacer
28740  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28741  * @constructor
28742  * Creates a new Spacer
28743  */
28744 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28745     // private
28746     render : function(td){
28747         td.style.width = '100%';
28748         Roo.Toolbar.Fill.superclass.render.call(this, td);
28749     }
28750 });
28751
28752 /**
28753  * @class Roo.Toolbar.TextItem
28754  * @extends Roo.Toolbar.Item
28755  * A simple class that renders text directly into a toolbar.
28756  * @constructor
28757  * Creates a new TextItem
28758  * @param {String} text
28759  */
28760 Roo.Toolbar.TextItem = function(cfg){
28761     var  text = cfg || "";
28762     if (typeof(cfg) == 'object') {
28763         text = cfg.text || "";
28764     }  else {
28765         cfg = null;
28766     }
28767     var s = document.createElement("span");
28768     s.className = "ytb-text";
28769     s.innerHTML = text;
28770     if (cfg) {
28771         cfg.el  = s;
28772     }
28773     
28774     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28775 };
28776 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28777     
28778      
28779     enable:Roo.emptyFn,
28780     disable:Roo.emptyFn,
28781     focus:Roo.emptyFn
28782 });
28783
28784 /**
28785  * @class Roo.Toolbar.Button
28786  * @extends Roo.Button
28787  * A button that renders into a toolbar.
28788  * @constructor
28789  * Creates a new Button
28790  * @param {Object} config A standard {@link Roo.Button} config object
28791  */
28792 Roo.Toolbar.Button = function(config){
28793     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28794 };
28795 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28796     render : function(td){
28797         this.td = td;
28798         Roo.Toolbar.Button.superclass.render.call(this, td);
28799     },
28800     
28801     /**
28802      * Removes and destroys this button
28803      */
28804     destroy : function(){
28805         Roo.Toolbar.Button.superclass.destroy.call(this);
28806         this.td.parentNode.removeChild(this.td);
28807     },
28808     
28809     /**
28810      * Shows this button
28811      */
28812     show: function(){
28813         this.hidden = false;
28814         this.td.style.display = "";
28815     },
28816     
28817     /**
28818      * Hides this button
28819      */
28820     hide: function(){
28821         this.hidden = true;
28822         this.td.style.display = "none";
28823     },
28824
28825     /**
28826      * Disables this item
28827      */
28828     disable : function(){
28829         Roo.fly(this.td).addClass("x-item-disabled");
28830         this.disabled = true;
28831     },
28832
28833     /**
28834      * Enables this item
28835      */
28836     enable : function(){
28837         Roo.fly(this.td).removeClass("x-item-disabled");
28838         this.disabled = false;
28839     }
28840 });
28841 // backwards compat
28842 Roo.ToolbarButton = Roo.Toolbar.Button;
28843
28844 /**
28845  * @class Roo.Toolbar.SplitButton
28846  * @extends Roo.SplitButton
28847  * A menu button that renders into a toolbar.
28848  * @constructor
28849  * Creates a new SplitButton
28850  * @param {Object} config A standard {@link Roo.SplitButton} config object
28851  */
28852 Roo.Toolbar.SplitButton = function(config){
28853     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28854 };
28855 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28856     render : function(td){
28857         this.td = td;
28858         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28859     },
28860     
28861     /**
28862      * Removes and destroys this button
28863      */
28864     destroy : function(){
28865         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28866         this.td.parentNode.removeChild(this.td);
28867     },
28868     
28869     /**
28870      * Shows this button
28871      */
28872     show: function(){
28873         this.hidden = false;
28874         this.td.style.display = "";
28875     },
28876     
28877     /**
28878      * Hides this button
28879      */
28880     hide: function(){
28881         this.hidden = true;
28882         this.td.style.display = "none";
28883     }
28884 });
28885
28886 // backwards compat
28887 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28888  * Based on:
28889  * Ext JS Library 1.1.1
28890  * Copyright(c) 2006-2007, Ext JS, LLC.
28891  *
28892  * Originally Released Under LGPL - original licence link has changed is not relivant.
28893  *
28894  * Fork - LGPL
28895  * <script type="text/javascript">
28896  */
28897  
28898 /**
28899  * @class Roo.PagingToolbar
28900  * @extends Roo.Toolbar
28901  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28902  * @constructor
28903  * Create a new PagingToolbar
28904  * @param {Object} config The config object
28905  */
28906 Roo.PagingToolbar = function(el, ds, config)
28907 {
28908     // old args format still supported... - xtype is prefered..
28909     if (typeof(el) == 'object' && el.xtype) {
28910         // created from xtype...
28911         config = el;
28912         ds = el.dataSource;
28913         el = config.container;
28914     }
28915     var items = [];
28916     if (config.items) {
28917         items = config.items;
28918         config.items = [];
28919     }
28920     
28921     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28922     this.ds = ds;
28923     this.cursor = 0;
28924     this.renderButtons(this.el);
28925     this.bind(ds);
28926     
28927     // supprot items array.
28928    
28929     Roo.each(items, function(e) {
28930         this.add(Roo.factory(e));
28931     },this);
28932     
28933 };
28934
28935 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28936     /**
28937      * @cfg {Roo.data.Store} dataSource
28938      * The underlying data store providing the paged data
28939      */
28940     /**
28941      * @cfg {String/HTMLElement/Element} container
28942      * container The id or element that will contain the toolbar
28943      */
28944     /**
28945      * @cfg {Boolean} displayInfo
28946      * True to display the displayMsg (defaults to false)
28947      */
28948     /**
28949      * @cfg {Number} pageSize
28950      * The number of records to display per page (defaults to 20)
28951      */
28952     pageSize: 20,
28953     /**
28954      * @cfg {String} displayMsg
28955      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28956      */
28957     displayMsg : 'Displaying {0} - {1} of {2}',
28958     /**
28959      * @cfg {String} emptyMsg
28960      * The message to display when no records are found (defaults to "No data to display")
28961      */
28962     emptyMsg : 'No data to display',
28963     /**
28964      * Customizable piece of the default paging text (defaults to "Page")
28965      * @type String
28966      */
28967     beforePageText : "Page",
28968     /**
28969      * Customizable piece of the default paging text (defaults to "of %0")
28970      * @type String
28971      */
28972     afterPageText : "of {0}",
28973     /**
28974      * Customizable piece of the default paging text (defaults to "First Page")
28975      * @type String
28976      */
28977     firstText : "First Page",
28978     /**
28979      * Customizable piece of the default paging text (defaults to "Previous Page")
28980      * @type String
28981      */
28982     prevText : "Previous Page",
28983     /**
28984      * Customizable piece of the default paging text (defaults to "Next Page")
28985      * @type String
28986      */
28987     nextText : "Next Page",
28988     /**
28989      * Customizable piece of the default paging text (defaults to "Last Page")
28990      * @type String
28991      */
28992     lastText : "Last Page",
28993     /**
28994      * Customizable piece of the default paging text (defaults to "Refresh")
28995      * @type String
28996      */
28997     refreshText : "Refresh",
28998
28999     // private
29000     renderButtons : function(el){
29001         Roo.PagingToolbar.superclass.render.call(this, el);
29002         this.first = this.addButton({
29003             tooltip: this.firstText,
29004             cls: "x-btn-icon x-grid-page-first",
29005             disabled: true,
29006             handler: this.onClick.createDelegate(this, ["first"])
29007         });
29008         this.prev = this.addButton({
29009             tooltip: this.prevText,
29010             cls: "x-btn-icon x-grid-page-prev",
29011             disabled: true,
29012             handler: this.onClick.createDelegate(this, ["prev"])
29013         });
29014         //this.addSeparator();
29015         this.add(this.beforePageText);
29016         this.field = Roo.get(this.addDom({
29017            tag: "input",
29018            type: "text",
29019            size: "3",
29020            value: "1",
29021            cls: "x-grid-page-number"
29022         }).el);
29023         this.field.on("keydown", this.onPagingKeydown, this);
29024         this.field.on("focus", function(){this.dom.select();});
29025         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29026         this.field.setHeight(18);
29027         //this.addSeparator();
29028         this.next = this.addButton({
29029             tooltip: this.nextText,
29030             cls: "x-btn-icon x-grid-page-next",
29031             disabled: true,
29032             handler: this.onClick.createDelegate(this, ["next"])
29033         });
29034         this.last = this.addButton({
29035             tooltip: this.lastText,
29036             cls: "x-btn-icon x-grid-page-last",
29037             disabled: true,
29038             handler: this.onClick.createDelegate(this, ["last"])
29039         });
29040         //this.addSeparator();
29041         this.loading = this.addButton({
29042             tooltip: this.refreshText,
29043             cls: "x-btn-icon x-grid-loading",
29044             handler: this.onClick.createDelegate(this, ["refresh"])
29045         });
29046
29047         if(this.displayInfo){
29048             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29049         }
29050     },
29051
29052     // private
29053     updateInfo : function(){
29054         if(this.displayEl){
29055             var count = this.ds.getCount();
29056             var msg = count == 0 ?
29057                 this.emptyMsg :
29058                 String.format(
29059                     this.displayMsg,
29060                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29061                 );
29062             this.displayEl.update(msg);
29063         }
29064     },
29065
29066     // private
29067     onLoad : function(ds, r, o){
29068        this.cursor = o.params ? o.params.start : 0;
29069        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29070
29071        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29072        this.field.dom.value = ap;
29073        this.first.setDisabled(ap == 1);
29074        this.prev.setDisabled(ap == 1);
29075        this.next.setDisabled(ap == ps);
29076        this.last.setDisabled(ap == ps);
29077        this.loading.enable();
29078        this.updateInfo();
29079     },
29080
29081     // private
29082     getPageData : function(){
29083         var total = this.ds.getTotalCount();
29084         return {
29085             total : total,
29086             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29087             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29088         };
29089     },
29090
29091     // private
29092     onLoadError : function(){
29093         this.loading.enable();
29094     },
29095
29096     // private
29097     onPagingKeydown : function(e){
29098         var k = e.getKey();
29099         var d = this.getPageData();
29100         if(k == e.RETURN){
29101             var v = this.field.dom.value, pageNum;
29102             if(!v || isNaN(pageNum = parseInt(v, 10))){
29103                 this.field.dom.value = d.activePage;
29104                 return;
29105             }
29106             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29107             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29108             e.stopEvent();
29109         }
29110         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))
29111         {
29112           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29113           this.field.dom.value = pageNum;
29114           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29115           e.stopEvent();
29116         }
29117         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29118         {
29119           var v = this.field.dom.value, pageNum; 
29120           var increment = (e.shiftKey) ? 10 : 1;
29121           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
29122             increment *= -1;
29123           }
29124           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29125             this.field.dom.value = d.activePage;
29126             return;
29127           }
29128           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29129           {
29130             this.field.dom.value = parseInt(v, 10) + increment;
29131             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29132             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29133           }
29134           e.stopEvent();
29135         }
29136     },
29137
29138     // private
29139     beforeLoad : function(){
29140         if(this.loading){
29141             this.loading.disable();
29142         }
29143     },
29144
29145     // private
29146     onClick : function(which){
29147         var ds = this.ds;
29148         switch(which){
29149             case "first":
29150                 ds.load({params:{start: 0, limit: this.pageSize}});
29151             break;
29152             case "prev":
29153                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29154             break;
29155             case "next":
29156                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29157             break;
29158             case "last":
29159                 var total = ds.getTotalCount();
29160                 var extra = total % this.pageSize;
29161                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29162                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29163             break;
29164             case "refresh":
29165                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29166             break;
29167         }
29168     },
29169
29170     /**
29171      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29172      * @param {Roo.data.Store} store The data store to unbind
29173      */
29174     unbind : function(ds){
29175         ds.un("beforeload", this.beforeLoad, this);
29176         ds.un("load", this.onLoad, this);
29177         ds.un("loadexception", this.onLoadError, this);
29178         ds.un("remove", this.updateInfo, this);
29179         ds.un("add", this.updateInfo, this);
29180         this.ds = undefined;
29181     },
29182
29183     /**
29184      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29185      * @param {Roo.data.Store} store The data store to bind
29186      */
29187     bind : function(ds){
29188         ds.on("beforeload", this.beforeLoad, this);
29189         ds.on("load", this.onLoad, this);
29190         ds.on("loadexception", this.onLoadError, this);
29191         ds.on("remove", this.updateInfo, this);
29192         ds.on("add", this.updateInfo, this);
29193         this.ds = ds;
29194     }
29195 });/*
29196  * Based on:
29197  * Ext JS Library 1.1.1
29198  * Copyright(c) 2006-2007, Ext JS, LLC.
29199  *
29200  * Originally Released Under LGPL - original licence link has changed is not relivant.
29201  *
29202  * Fork - LGPL
29203  * <script type="text/javascript">
29204  */
29205
29206 /**
29207  * @class Roo.Resizable
29208  * @extends Roo.util.Observable
29209  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29210  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29211  * 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
29212  * the element will be wrapped for you automatically.</p>
29213  * <p>Here is the list of valid resize handles:</p>
29214  * <pre>
29215 Value   Description
29216 ------  -------------------
29217  'n'     north
29218  's'     south
29219  'e'     east
29220  'w'     west
29221  'nw'    northwest
29222  'sw'    southwest
29223  'se'    southeast
29224  'ne'    northeast
29225  'hd'    horizontal drag
29226  'all'   all
29227 </pre>
29228  * <p>Here's an example showing the creation of a typical Resizable:</p>
29229  * <pre><code>
29230 var resizer = new Roo.Resizable("element-id", {
29231     handles: 'all',
29232     minWidth: 200,
29233     minHeight: 100,
29234     maxWidth: 500,
29235     maxHeight: 400,
29236     pinned: true
29237 });
29238 resizer.on("resize", myHandler);
29239 </code></pre>
29240  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29241  * resizer.east.setDisplayed(false);</p>
29242  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29243  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29244  * resize operation's new size (defaults to [0, 0])
29245  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29246  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29247  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29248  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29249  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29250  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29251  * @cfg {Number} width The width of the element in pixels (defaults to null)
29252  * @cfg {Number} height The height of the element in pixels (defaults to null)
29253  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29254  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29255  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29256  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29257  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29258  * in favor of the handles config option (defaults to false)
29259  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29260  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29261  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29262  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29263  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29264  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29265  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29266  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29267  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29268  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29269  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29270  * @constructor
29271  * Create a new resizable component
29272  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29273  * @param {Object} config configuration options
29274   */
29275 Roo.Resizable = function(el, config)
29276 {
29277     this.el = Roo.get(el);
29278
29279     if(config && config.wrap){
29280         config.resizeChild = this.el;
29281         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29282         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29283         this.el.setStyle("overflow", "hidden");
29284         this.el.setPositioning(config.resizeChild.getPositioning());
29285         config.resizeChild.clearPositioning();
29286         if(!config.width || !config.height){
29287             var csize = config.resizeChild.getSize();
29288             this.el.setSize(csize.width, csize.height);
29289         }
29290         if(config.pinned && !config.adjustments){
29291             config.adjustments = "auto";
29292         }
29293     }
29294
29295     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29296     this.proxy.unselectable();
29297     this.proxy.enableDisplayMode('block');
29298
29299     Roo.apply(this, config);
29300
29301     if(this.pinned){
29302         this.disableTrackOver = true;
29303         this.el.addClass("x-resizable-pinned");
29304     }
29305     // if the element isn't positioned, make it relative
29306     var position = this.el.getStyle("position");
29307     if(position != "absolute" && position != "fixed"){
29308         this.el.setStyle("position", "relative");
29309     }
29310     if(!this.handles){ // no handles passed, must be legacy style
29311         this.handles = 's,e,se';
29312         if(this.multiDirectional){
29313             this.handles += ',n,w';
29314         }
29315     }
29316     if(this.handles == "all"){
29317         this.handles = "n s e w ne nw se sw";
29318     }
29319     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29320     var ps = Roo.Resizable.positions;
29321     for(var i = 0, len = hs.length; i < len; i++){
29322         if(hs[i] && ps[hs[i]]){
29323             var pos = ps[hs[i]];
29324             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29325         }
29326     }
29327     // legacy
29328     this.corner = this.southeast;
29329     
29330     // updateBox = the box can move..
29331     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29332         this.updateBox = true;
29333     }
29334
29335     this.activeHandle = null;
29336
29337     if(this.resizeChild){
29338         if(typeof this.resizeChild == "boolean"){
29339             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29340         }else{
29341             this.resizeChild = Roo.get(this.resizeChild, true);
29342         }
29343     }
29344     
29345     if(this.adjustments == "auto"){
29346         var rc = this.resizeChild;
29347         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29348         if(rc && (hw || hn)){
29349             rc.position("relative");
29350             rc.setLeft(hw ? hw.el.getWidth() : 0);
29351             rc.setTop(hn ? hn.el.getHeight() : 0);
29352         }
29353         this.adjustments = [
29354             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29355             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29356         ];
29357     }
29358
29359     if(this.draggable){
29360         this.dd = this.dynamic ?
29361             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29362         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29363     }
29364
29365     // public events
29366     this.addEvents({
29367         /**
29368          * @event beforeresize
29369          * Fired before resize is allowed. Set enabled to false to cancel resize.
29370          * @param {Roo.Resizable} this
29371          * @param {Roo.EventObject} e The mousedown event
29372          */
29373         "beforeresize" : true,
29374         /**
29375          * @event resizing
29376          * Fired a resizing.
29377          * @param {Roo.Resizable} this
29378          * @param {Number} x The new x position
29379          * @param {Number} y The new y position
29380          * @param {Number} w The new w width
29381          * @param {Number} h The new h hight
29382          * @param {Roo.EventObject} e The mouseup event
29383          */
29384         "resizing" : true,
29385         /**
29386          * @event resize
29387          * Fired after a resize.
29388          * @param {Roo.Resizable} this
29389          * @param {Number} width The new width
29390          * @param {Number} height The new height
29391          * @param {Roo.EventObject} e The mouseup event
29392          */
29393         "resize" : true
29394     });
29395
29396     if(this.width !== null && this.height !== null){
29397         this.resizeTo(this.width, this.height);
29398     }else{
29399         this.updateChildSize();
29400     }
29401     if(Roo.isIE){
29402         this.el.dom.style.zoom = 1;
29403     }
29404     Roo.Resizable.superclass.constructor.call(this);
29405 };
29406
29407 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29408         resizeChild : false,
29409         adjustments : [0, 0],
29410         minWidth : 5,
29411         minHeight : 5,
29412         maxWidth : 10000,
29413         maxHeight : 10000,
29414         enabled : true,
29415         animate : false,
29416         duration : .35,
29417         dynamic : false,
29418         handles : false,
29419         multiDirectional : false,
29420         disableTrackOver : false,
29421         easing : 'easeOutStrong',
29422         widthIncrement : 0,
29423         heightIncrement : 0,
29424         pinned : false,
29425         width : null,
29426         height : null,
29427         preserveRatio : false,
29428         transparent: false,
29429         minX: 0,
29430         minY: 0,
29431         draggable: false,
29432
29433         /**
29434          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29435          */
29436         constrainTo: undefined,
29437         /**
29438          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29439          */
29440         resizeRegion: undefined,
29441
29442
29443     /**
29444      * Perform a manual resize
29445      * @param {Number} width
29446      * @param {Number} height
29447      */
29448     resizeTo : function(width, height){
29449         this.el.setSize(width, height);
29450         this.updateChildSize();
29451         this.fireEvent("resize", this, width, height, null);
29452     },
29453
29454     // private
29455     startSizing : function(e, handle){
29456         this.fireEvent("beforeresize", this, e);
29457         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29458
29459             if(!this.overlay){
29460                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29461                 this.overlay.unselectable();
29462                 this.overlay.enableDisplayMode("block");
29463                 this.overlay.on("mousemove", this.onMouseMove, this);
29464                 this.overlay.on("mouseup", this.onMouseUp, this);
29465             }
29466             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29467
29468             this.resizing = true;
29469             this.startBox = this.el.getBox();
29470             this.startPoint = e.getXY();
29471             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29472                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29473
29474             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29475             this.overlay.show();
29476
29477             if(this.constrainTo) {
29478                 var ct = Roo.get(this.constrainTo);
29479                 this.resizeRegion = ct.getRegion().adjust(
29480                     ct.getFrameWidth('t'),
29481                     ct.getFrameWidth('l'),
29482                     -ct.getFrameWidth('b'),
29483                     -ct.getFrameWidth('r')
29484                 );
29485             }
29486
29487             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29488             this.proxy.show();
29489             this.proxy.setBox(this.startBox);
29490             if(!this.dynamic){
29491                 this.proxy.setStyle('visibility', 'visible');
29492             }
29493         }
29494     },
29495
29496     // private
29497     onMouseDown : function(handle, e){
29498         if(this.enabled){
29499             e.stopEvent();
29500             this.activeHandle = handle;
29501             this.startSizing(e, handle);
29502         }
29503     },
29504
29505     // private
29506     onMouseUp : function(e){
29507         var size = this.resizeElement();
29508         this.resizing = false;
29509         this.handleOut();
29510         this.overlay.hide();
29511         this.proxy.hide();
29512         this.fireEvent("resize", this, size.width, size.height, e);
29513     },
29514
29515     // private
29516     updateChildSize : function(){
29517         
29518         if(this.resizeChild){
29519             var el = this.el;
29520             var child = this.resizeChild;
29521             var adj = this.adjustments;
29522             if(el.dom.offsetWidth){
29523                 var b = el.getSize(true);
29524                 child.setSize(b.width+adj[0], b.height+adj[1]);
29525             }
29526             // Second call here for IE
29527             // The first call enables instant resizing and
29528             // the second call corrects scroll bars if they
29529             // exist
29530             if(Roo.isIE){
29531                 setTimeout(function(){
29532                     if(el.dom.offsetWidth){
29533                         var b = el.getSize(true);
29534                         child.setSize(b.width+adj[0], b.height+adj[1]);
29535                     }
29536                 }, 10);
29537             }
29538         }
29539     },
29540
29541     // private
29542     snap : function(value, inc, min){
29543         if(!inc || !value) {
29544             return value;
29545         }
29546         var newValue = value;
29547         var m = value % inc;
29548         if(m > 0){
29549             if(m > (inc/2)){
29550                 newValue = value + (inc-m);
29551             }else{
29552                 newValue = value - m;
29553             }
29554         }
29555         return Math.max(min, newValue);
29556     },
29557
29558     // private
29559     resizeElement : function(){
29560         var box = this.proxy.getBox();
29561         if(this.updateBox){
29562             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29563         }else{
29564             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29565         }
29566         this.updateChildSize();
29567         if(!this.dynamic){
29568             this.proxy.hide();
29569         }
29570         return box;
29571     },
29572
29573     // private
29574     constrain : function(v, diff, m, mx){
29575         if(v - diff < m){
29576             diff = v - m;
29577         }else if(v - diff > mx){
29578             diff = mx - v;
29579         }
29580         return diff;
29581     },
29582
29583     // private
29584     onMouseMove : function(e){
29585         
29586         if(this.enabled){
29587             try{// try catch so if something goes wrong the user doesn't get hung
29588
29589             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29590                 return;
29591             }
29592
29593             //var curXY = this.startPoint;
29594             var curSize = this.curSize || this.startBox;
29595             var x = this.startBox.x, y = this.startBox.y;
29596             var ox = x, oy = y;
29597             var w = curSize.width, h = curSize.height;
29598             var ow = w, oh = h;
29599             var mw = this.minWidth, mh = this.minHeight;
29600             var mxw = this.maxWidth, mxh = this.maxHeight;
29601             var wi = this.widthIncrement;
29602             var hi = this.heightIncrement;
29603
29604             var eventXY = e.getXY();
29605             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29606             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29607
29608             var pos = this.activeHandle.position;
29609
29610             switch(pos){
29611                 case "east":
29612                     w += diffX;
29613                     w = Math.min(Math.max(mw, w), mxw);
29614                     break;
29615              
29616                 case "south":
29617                     h += diffY;
29618                     h = Math.min(Math.max(mh, h), mxh);
29619                     break;
29620                 case "southeast":
29621                     w += diffX;
29622                     h += diffY;
29623                     w = Math.min(Math.max(mw, w), mxw);
29624                     h = Math.min(Math.max(mh, h), mxh);
29625                     break;
29626                 case "north":
29627                     diffY = this.constrain(h, diffY, mh, mxh);
29628                     y += diffY;
29629                     h -= diffY;
29630                     break;
29631                 case "hdrag":
29632                     
29633                     if (wi) {
29634                         var adiffX = Math.abs(diffX);
29635                         var sub = (adiffX % wi); // how much 
29636                         if (sub > (wi/2)) { // far enough to snap
29637                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29638                         } else {
29639                             // remove difference.. 
29640                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29641                         }
29642                     }
29643                     x += diffX;
29644                     x = Math.max(this.minX, x);
29645                     break;
29646                 case "west":
29647                     diffX = this.constrain(w, diffX, mw, mxw);
29648                     x += diffX;
29649                     w -= diffX;
29650                     break;
29651                 case "northeast":
29652                     w += diffX;
29653                     w = Math.min(Math.max(mw, w), mxw);
29654                     diffY = this.constrain(h, diffY, mh, mxh);
29655                     y += diffY;
29656                     h -= diffY;
29657                     break;
29658                 case "northwest":
29659                     diffX = this.constrain(w, diffX, mw, mxw);
29660                     diffY = this.constrain(h, diffY, mh, mxh);
29661                     y += diffY;
29662                     h -= diffY;
29663                     x += diffX;
29664                     w -= diffX;
29665                     break;
29666                case "southwest":
29667                     diffX = this.constrain(w, diffX, mw, mxw);
29668                     h += diffY;
29669                     h = Math.min(Math.max(mh, h), mxh);
29670                     x += diffX;
29671                     w -= diffX;
29672                     break;
29673             }
29674
29675             var sw = this.snap(w, wi, mw);
29676             var sh = this.snap(h, hi, mh);
29677             if(sw != w || sh != h){
29678                 switch(pos){
29679                     case "northeast":
29680                         y -= sh - h;
29681                     break;
29682                     case "north":
29683                         y -= sh - h;
29684                         break;
29685                     case "southwest":
29686                         x -= sw - w;
29687                     break;
29688                     case "west":
29689                         x -= sw - w;
29690                         break;
29691                     case "northwest":
29692                         x -= sw - w;
29693                         y -= sh - h;
29694                     break;
29695                 }
29696                 w = sw;
29697                 h = sh;
29698             }
29699
29700             if(this.preserveRatio){
29701                 switch(pos){
29702                     case "southeast":
29703                     case "east":
29704                         h = oh * (w/ow);
29705                         h = Math.min(Math.max(mh, h), mxh);
29706                         w = ow * (h/oh);
29707                        break;
29708                     case "south":
29709                         w = ow * (h/oh);
29710                         w = Math.min(Math.max(mw, w), mxw);
29711                         h = oh * (w/ow);
29712                         break;
29713                     case "northeast":
29714                         w = ow * (h/oh);
29715                         w = Math.min(Math.max(mw, w), mxw);
29716                         h = oh * (w/ow);
29717                     break;
29718                     case "north":
29719                         var tw = w;
29720                         w = ow * (h/oh);
29721                         w = Math.min(Math.max(mw, w), mxw);
29722                         h = oh * (w/ow);
29723                         x += (tw - w) / 2;
29724                         break;
29725                     case "southwest":
29726                         h = oh * (w/ow);
29727                         h = Math.min(Math.max(mh, h), mxh);
29728                         var tw = w;
29729                         w = ow * (h/oh);
29730                         x += tw - w;
29731                         break;
29732                     case "west":
29733                         var th = h;
29734                         h = oh * (w/ow);
29735                         h = Math.min(Math.max(mh, h), mxh);
29736                         y += (th - h) / 2;
29737                         var tw = w;
29738                         w = ow * (h/oh);
29739                         x += tw - w;
29740                        break;
29741                     case "northwest":
29742                         var tw = w;
29743                         var th = h;
29744                         h = oh * (w/ow);
29745                         h = Math.min(Math.max(mh, h), mxh);
29746                         w = ow * (h/oh);
29747                         y += th - h;
29748                         x += tw - w;
29749                        break;
29750
29751                 }
29752             }
29753             if (pos == 'hdrag') {
29754                 w = ow;
29755             }
29756             this.proxy.setBounds(x, y, w, h);
29757             if(this.dynamic){
29758                 this.resizeElement();
29759             }
29760             }catch(e){}
29761         }
29762         this.fireEvent("resizing", this, x, y, w, h, e);
29763     },
29764
29765     // private
29766     handleOver : function(){
29767         if(this.enabled){
29768             this.el.addClass("x-resizable-over");
29769         }
29770     },
29771
29772     // private
29773     handleOut : function(){
29774         if(!this.resizing){
29775             this.el.removeClass("x-resizable-over");
29776         }
29777     },
29778
29779     /**
29780      * Returns the element this component is bound to.
29781      * @return {Roo.Element}
29782      */
29783     getEl : function(){
29784         return this.el;
29785     },
29786
29787     /**
29788      * Returns the resizeChild element (or null).
29789      * @return {Roo.Element}
29790      */
29791     getResizeChild : function(){
29792         return this.resizeChild;
29793     },
29794     groupHandler : function()
29795     {
29796         
29797     },
29798     /**
29799      * Destroys this resizable. If the element was wrapped and
29800      * removeEl is not true then the element remains.
29801      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29802      */
29803     destroy : function(removeEl){
29804         this.proxy.remove();
29805         if(this.overlay){
29806             this.overlay.removeAllListeners();
29807             this.overlay.remove();
29808         }
29809         var ps = Roo.Resizable.positions;
29810         for(var k in ps){
29811             if(typeof ps[k] != "function" && this[ps[k]]){
29812                 var h = this[ps[k]];
29813                 h.el.removeAllListeners();
29814                 h.el.remove();
29815             }
29816         }
29817         if(removeEl){
29818             this.el.update("");
29819             this.el.remove();
29820         }
29821     }
29822 });
29823
29824 // private
29825 // hash to map config positions to true positions
29826 Roo.Resizable.positions = {
29827     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29828     hd: "hdrag"
29829 };
29830
29831 // private
29832 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29833     if(!this.tpl){
29834         // only initialize the template if resizable is used
29835         var tpl = Roo.DomHelper.createTemplate(
29836             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29837         );
29838         tpl.compile();
29839         Roo.Resizable.Handle.prototype.tpl = tpl;
29840     }
29841     this.position = pos;
29842     this.rz = rz;
29843     // show north drag fro topdra
29844     var handlepos = pos == 'hdrag' ? 'north' : pos;
29845     
29846     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29847     if (pos == 'hdrag') {
29848         this.el.setStyle('cursor', 'pointer');
29849     }
29850     this.el.unselectable();
29851     if(transparent){
29852         this.el.setOpacity(0);
29853     }
29854     this.el.on("mousedown", this.onMouseDown, this);
29855     if(!disableTrackOver){
29856         this.el.on("mouseover", this.onMouseOver, this);
29857         this.el.on("mouseout", this.onMouseOut, this);
29858     }
29859 };
29860
29861 // private
29862 Roo.Resizable.Handle.prototype = {
29863     afterResize : function(rz){
29864         Roo.log('after?');
29865         // do nothing
29866     },
29867     // private
29868     onMouseDown : function(e){
29869         this.rz.onMouseDown(this, e);
29870     },
29871     // private
29872     onMouseOver : function(e){
29873         this.rz.handleOver(this, e);
29874     },
29875     // private
29876     onMouseOut : function(e){
29877         this.rz.handleOut(this, e);
29878     }
29879 };/*
29880  * Based on:
29881  * Ext JS Library 1.1.1
29882  * Copyright(c) 2006-2007, Ext JS, LLC.
29883  *
29884  * Originally Released Under LGPL - original licence link has changed is not relivant.
29885  *
29886  * Fork - LGPL
29887  * <script type="text/javascript">
29888  */
29889
29890 /**
29891  * @class Roo.Editor
29892  * @extends Roo.Component
29893  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29894  * @constructor
29895  * Create a new Editor
29896  * @param {Roo.form.Field} field The Field object (or descendant)
29897  * @param {Object} config The config object
29898  */
29899 Roo.Editor = function(field, config){
29900     Roo.Editor.superclass.constructor.call(this, config);
29901     this.field = field;
29902     this.addEvents({
29903         /**
29904              * @event beforestartedit
29905              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29906              * false from the handler of this event.
29907              * @param {Editor} this
29908              * @param {Roo.Element} boundEl The underlying element bound to this editor
29909              * @param {Mixed} value The field value being set
29910              */
29911         "beforestartedit" : true,
29912         /**
29913              * @event startedit
29914              * Fires when this editor is displayed
29915              * @param {Roo.Element} boundEl The underlying element bound to this editor
29916              * @param {Mixed} value The starting field value
29917              */
29918         "startedit" : true,
29919         /**
29920              * @event beforecomplete
29921              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29922              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29923              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29924              * event will not fire since no edit actually occurred.
29925              * @param {Editor} this
29926              * @param {Mixed} value The current field value
29927              * @param {Mixed} startValue The original field value
29928              */
29929         "beforecomplete" : true,
29930         /**
29931              * @event complete
29932              * Fires after editing is complete and any changed value has been written to the underlying field.
29933              * @param {Editor} this
29934              * @param {Mixed} value The current field value
29935              * @param {Mixed} startValue The original field value
29936              */
29937         "complete" : true,
29938         /**
29939          * @event specialkey
29940          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29941          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29942          * @param {Roo.form.Field} this
29943          * @param {Roo.EventObject} e The event object
29944          */
29945         "specialkey" : true
29946     });
29947 };
29948
29949 Roo.extend(Roo.Editor, Roo.Component, {
29950     /**
29951      * @cfg {Boolean/String} autosize
29952      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29953      * or "height" to adopt the height only (defaults to false)
29954      */
29955     /**
29956      * @cfg {Boolean} revertInvalid
29957      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29958      * validation fails (defaults to true)
29959      */
29960     /**
29961      * @cfg {Boolean} ignoreNoChange
29962      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29963      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29964      * will never be ignored.
29965      */
29966     /**
29967      * @cfg {Boolean} hideEl
29968      * False to keep the bound element visible while the editor is displayed (defaults to true)
29969      */
29970     /**
29971      * @cfg {Mixed} value
29972      * The data value of the underlying field (defaults to "")
29973      */
29974     value : "",
29975     /**
29976      * @cfg {String} alignment
29977      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29978      */
29979     alignment: "c-c?",
29980     /**
29981      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29982      * for bottom-right shadow (defaults to "frame")
29983      */
29984     shadow : "frame",
29985     /**
29986      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29987      */
29988     constrain : false,
29989     /**
29990      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29991      */
29992     completeOnEnter : false,
29993     /**
29994      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29995      */
29996     cancelOnEsc : false,
29997     /**
29998      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29999      */
30000     updateEl : false,
30001
30002     // private
30003     onRender : function(ct, position){
30004         this.el = new Roo.Layer({
30005             shadow: this.shadow,
30006             cls: "x-editor",
30007             parentEl : ct,
30008             shim : this.shim,
30009             shadowOffset:4,
30010             id: this.id,
30011             constrain: this.constrain
30012         });
30013         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
30014         if(this.field.msgTarget != 'title'){
30015             this.field.msgTarget = 'qtip';
30016         }
30017         this.field.render(this.el);
30018         if(Roo.isGecko){
30019             this.field.el.dom.setAttribute('autocomplete', 'off');
30020         }
30021         this.field.on("specialkey", this.onSpecialKey, this);
30022         if(this.swallowKeys){
30023             this.field.el.swallowEvent(['keydown','keypress']);
30024         }
30025         this.field.show();
30026         this.field.on("blur", this.onBlur, this);
30027         if(this.field.grow){
30028             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30029         }
30030     },
30031
30032     onSpecialKey : function(field, e)
30033     {
30034         //Roo.log('editor onSpecialKey');
30035         if(this.completeOnEnter && e.getKey() == e.ENTER){
30036             e.stopEvent();
30037             this.completeEdit();
30038             return;
30039         }
30040         // do not fire special key otherwise it might hide close the editor...
30041         if(e.getKey() == e.ENTER){    
30042             return;
30043         }
30044         if(this.cancelOnEsc && e.getKey() == e.ESC){
30045             this.cancelEdit();
30046             return;
30047         } 
30048         this.fireEvent('specialkey', field, e);
30049     
30050     },
30051
30052     /**
30053      * Starts the editing process and shows the editor.
30054      * @param {String/HTMLElement/Element} el The element to edit
30055      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30056       * to the innerHTML of el.
30057      */
30058     startEdit : function(el, value){
30059         if(this.editing){
30060             this.completeEdit();
30061         }
30062         this.boundEl = Roo.get(el);
30063         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30064         if(!this.rendered){
30065             this.render(this.parentEl || document.body);
30066         }
30067         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30068             return;
30069         }
30070         this.startValue = v;
30071         this.field.setValue(v);
30072         if(this.autoSize){
30073             var sz = this.boundEl.getSize();
30074             switch(this.autoSize){
30075                 case "width":
30076                 this.setSize(sz.width,  "");
30077                 break;
30078                 case "height":
30079                 this.setSize("",  sz.height);
30080                 break;
30081                 default:
30082                 this.setSize(sz.width,  sz.height);
30083             }
30084         }
30085         this.el.alignTo(this.boundEl, this.alignment);
30086         this.editing = true;
30087         if(Roo.QuickTips){
30088             Roo.QuickTips.disable();
30089         }
30090         this.show();
30091     },
30092
30093     /**
30094      * Sets the height and width of this editor.
30095      * @param {Number} width The new width
30096      * @param {Number} height The new height
30097      */
30098     setSize : function(w, h){
30099         this.field.setSize(w, h);
30100         if(this.el){
30101             this.el.sync();
30102         }
30103     },
30104
30105     /**
30106      * Realigns the editor to the bound field based on the current alignment config value.
30107      */
30108     realign : function(){
30109         this.el.alignTo(this.boundEl, this.alignment);
30110     },
30111
30112     /**
30113      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30114      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30115      */
30116     completeEdit : function(remainVisible){
30117         if(!this.editing){
30118             return;
30119         }
30120         var v = this.getValue();
30121         if(this.revertInvalid !== false && !this.field.isValid()){
30122             v = this.startValue;
30123             this.cancelEdit(true);
30124         }
30125         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30126             this.editing = false;
30127             this.hide();
30128             return;
30129         }
30130         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30131             this.editing = false;
30132             if(this.updateEl && this.boundEl){
30133                 this.boundEl.update(v);
30134             }
30135             if(remainVisible !== true){
30136                 this.hide();
30137             }
30138             this.fireEvent("complete", this, v, this.startValue);
30139         }
30140     },
30141
30142     // private
30143     onShow : function(){
30144         this.el.show();
30145         if(this.hideEl !== false){
30146             this.boundEl.hide();
30147         }
30148         this.field.show();
30149         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30150             this.fixIEFocus = true;
30151             this.deferredFocus.defer(50, this);
30152         }else{
30153             this.field.focus();
30154         }
30155         this.fireEvent("startedit", this.boundEl, this.startValue);
30156     },
30157
30158     deferredFocus : function(){
30159         if(this.editing){
30160             this.field.focus();
30161         }
30162     },
30163
30164     /**
30165      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30166      * reverted to the original starting value.
30167      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30168      * cancel (defaults to false)
30169      */
30170     cancelEdit : function(remainVisible){
30171         if(this.editing){
30172             this.setValue(this.startValue);
30173             if(remainVisible !== true){
30174                 this.hide();
30175             }
30176         }
30177     },
30178
30179     // private
30180     onBlur : function(){
30181         if(this.allowBlur !== true && this.editing){
30182             this.completeEdit();
30183         }
30184     },
30185
30186     // private
30187     onHide : function(){
30188         if(this.editing){
30189             this.completeEdit();
30190             return;
30191         }
30192         this.field.blur();
30193         if(this.field.collapse){
30194             this.field.collapse();
30195         }
30196         this.el.hide();
30197         if(this.hideEl !== false){
30198             this.boundEl.show();
30199         }
30200         if(Roo.QuickTips){
30201             Roo.QuickTips.enable();
30202         }
30203     },
30204
30205     /**
30206      * Sets the data value of the editor
30207      * @param {Mixed} value Any valid value supported by the underlying field
30208      */
30209     setValue : function(v){
30210         this.field.setValue(v);
30211     },
30212
30213     /**
30214      * Gets the data value of the editor
30215      * @return {Mixed} The data value
30216      */
30217     getValue : function(){
30218         return this.field.getValue();
30219     }
30220 });/*
30221  * Based on:
30222  * Ext JS Library 1.1.1
30223  * Copyright(c) 2006-2007, Ext JS, LLC.
30224  *
30225  * Originally Released Under LGPL - original licence link has changed is not relivant.
30226  *
30227  * Fork - LGPL
30228  * <script type="text/javascript">
30229  */
30230  
30231 /**
30232  * @class Roo.BasicDialog
30233  * @extends Roo.util.Observable
30234  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30235  * <pre><code>
30236 var dlg = new Roo.BasicDialog("my-dlg", {
30237     height: 200,
30238     width: 300,
30239     minHeight: 100,
30240     minWidth: 150,
30241     modal: true,
30242     proxyDrag: true,
30243     shadow: true
30244 });
30245 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30246 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30247 dlg.addButton('Cancel', dlg.hide, dlg);
30248 dlg.show();
30249 </code></pre>
30250   <b>A Dialog should always be a direct child of the body element.</b>
30251  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30252  * @cfg {String} title Default text to display in the title bar (defaults to null)
30253  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30254  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30255  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30256  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30257  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30258  * (defaults to null with no animation)
30259  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30260  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30261  * property for valid values (defaults to 'all')
30262  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30263  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30264  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30265  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30266  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30267  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30268  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30269  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30270  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30271  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30272  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30273  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30274  * draggable = true (defaults to false)
30275  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30276  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30277  * shadow (defaults to false)
30278  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30279  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30280  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30281  * @cfg {Array} buttons Array of buttons
30282  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30283  * @constructor
30284  * Create a new BasicDialog.
30285  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30286  * @param {Object} config Configuration options
30287  */
30288 Roo.BasicDialog = function(el, config){
30289     this.el = Roo.get(el);
30290     var dh = Roo.DomHelper;
30291     if(!this.el && config && config.autoCreate){
30292         if(typeof config.autoCreate == "object"){
30293             if(!config.autoCreate.id){
30294                 config.autoCreate.id = el;
30295             }
30296             this.el = dh.append(document.body,
30297                         config.autoCreate, true);
30298         }else{
30299             this.el = dh.append(document.body,
30300                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30301         }
30302     }
30303     el = this.el;
30304     el.setDisplayed(true);
30305     el.hide = this.hideAction;
30306     this.id = el.id;
30307     el.addClass("x-dlg");
30308
30309     Roo.apply(this, config);
30310
30311     this.proxy = el.createProxy("x-dlg-proxy");
30312     this.proxy.hide = this.hideAction;
30313     this.proxy.setOpacity(.5);
30314     this.proxy.hide();
30315
30316     if(config.width){
30317         el.setWidth(config.width);
30318     }
30319     if(config.height){
30320         el.setHeight(config.height);
30321     }
30322     this.size = el.getSize();
30323     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30324         this.xy = [config.x,config.y];
30325     }else{
30326         this.xy = el.getCenterXY(true);
30327     }
30328     /** The header element @type Roo.Element */
30329     this.header = el.child("> .x-dlg-hd");
30330     /** The body element @type Roo.Element */
30331     this.body = el.child("> .x-dlg-bd");
30332     /** The footer element @type Roo.Element */
30333     this.footer = el.child("> .x-dlg-ft");
30334
30335     if(!this.header){
30336         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30337     }
30338     if(!this.body){
30339         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30340     }
30341
30342     this.header.unselectable();
30343     if(this.title){
30344         this.header.update(this.title);
30345     }
30346     // this element allows the dialog to be focused for keyboard event
30347     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30348     this.focusEl.swallowEvent("click", true);
30349
30350     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30351
30352     // wrap the body and footer for special rendering
30353     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30354     if(this.footer){
30355         this.bwrap.dom.appendChild(this.footer.dom);
30356     }
30357
30358     this.bg = this.el.createChild({
30359         tag: "div", cls:"x-dlg-bg",
30360         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30361     });
30362     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30363
30364
30365     if(this.autoScroll !== false && !this.autoTabs){
30366         this.body.setStyle("overflow", "auto");
30367     }
30368
30369     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30370
30371     if(this.closable !== false){
30372         this.el.addClass("x-dlg-closable");
30373         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30374         this.close.on("click", this.closeClick, this);
30375         this.close.addClassOnOver("x-dlg-close-over");
30376     }
30377     if(this.collapsible !== false){
30378         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30379         this.collapseBtn.on("click", this.collapseClick, this);
30380         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30381         this.header.on("dblclick", this.collapseClick, this);
30382     }
30383     if(this.resizable !== false){
30384         this.el.addClass("x-dlg-resizable");
30385         this.resizer = new Roo.Resizable(el, {
30386             minWidth: this.minWidth || 80,
30387             minHeight:this.minHeight || 80,
30388             handles: this.resizeHandles || "all",
30389             pinned: true
30390         });
30391         this.resizer.on("beforeresize", this.beforeResize, this);
30392         this.resizer.on("resize", this.onResize, this);
30393     }
30394     if(this.draggable !== false){
30395         el.addClass("x-dlg-draggable");
30396         if (!this.proxyDrag) {
30397             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30398         }
30399         else {
30400             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30401         }
30402         dd.setHandleElId(this.header.id);
30403         dd.endDrag = this.endMove.createDelegate(this);
30404         dd.startDrag = this.startMove.createDelegate(this);
30405         dd.onDrag = this.onDrag.createDelegate(this);
30406         dd.scroll = false;
30407         this.dd = dd;
30408     }
30409     if(this.modal){
30410         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30411         this.mask.enableDisplayMode("block");
30412         this.mask.hide();
30413         this.el.addClass("x-dlg-modal");
30414     }
30415     if(this.shadow){
30416         this.shadow = new Roo.Shadow({
30417             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30418             offset : this.shadowOffset
30419         });
30420     }else{
30421         this.shadowOffset = 0;
30422     }
30423     if(Roo.useShims && this.shim !== false){
30424         this.shim = this.el.createShim();
30425         this.shim.hide = this.hideAction;
30426         this.shim.hide();
30427     }else{
30428         this.shim = false;
30429     }
30430     if(this.autoTabs){
30431         this.initTabs();
30432     }
30433     if (this.buttons) { 
30434         var bts= this.buttons;
30435         this.buttons = [];
30436         Roo.each(bts, function(b) {
30437             this.addButton(b);
30438         }, this);
30439     }
30440     
30441     
30442     this.addEvents({
30443         /**
30444          * @event keydown
30445          * Fires when a key is pressed
30446          * @param {Roo.BasicDialog} this
30447          * @param {Roo.EventObject} e
30448          */
30449         "keydown" : true,
30450         /**
30451          * @event move
30452          * Fires when this dialog is moved by the user.
30453          * @param {Roo.BasicDialog} this
30454          * @param {Number} x The new page X
30455          * @param {Number} y The new page Y
30456          */
30457         "move" : true,
30458         /**
30459          * @event resize
30460          * Fires when this dialog is resized by the user.
30461          * @param {Roo.BasicDialog} this
30462          * @param {Number} width The new width
30463          * @param {Number} height The new height
30464          */
30465         "resize" : true,
30466         /**
30467          * @event beforehide
30468          * Fires before this dialog is hidden.
30469          * @param {Roo.BasicDialog} this
30470          */
30471         "beforehide" : true,
30472         /**
30473          * @event hide
30474          * Fires when this dialog is hidden.
30475          * @param {Roo.BasicDialog} this
30476          */
30477         "hide" : true,
30478         /**
30479          * @event beforeshow
30480          * Fires before this dialog is shown.
30481          * @param {Roo.BasicDialog} this
30482          */
30483         "beforeshow" : true,
30484         /**
30485          * @event show
30486          * Fires when this dialog is shown.
30487          * @param {Roo.BasicDialog} this
30488          */
30489         "show" : true
30490     });
30491     el.on("keydown", this.onKeyDown, this);
30492     el.on("mousedown", this.toFront, this);
30493     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30494     this.el.hide();
30495     Roo.DialogManager.register(this);
30496     Roo.BasicDialog.superclass.constructor.call(this);
30497 };
30498
30499 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30500     shadowOffset: Roo.isIE ? 6 : 5,
30501     minHeight: 80,
30502     minWidth: 200,
30503     minButtonWidth: 75,
30504     defaultButton: null,
30505     buttonAlign: "right",
30506     tabTag: 'div',
30507     firstShow: true,
30508
30509     /**
30510      * Sets the dialog title text
30511      * @param {String} text The title text to display
30512      * @return {Roo.BasicDialog} this
30513      */
30514     setTitle : function(text){
30515         this.header.update(text);
30516         return this;
30517     },
30518
30519     // private
30520     closeClick : function(){
30521         this.hide();
30522     },
30523
30524     // private
30525     collapseClick : function(){
30526         this[this.collapsed ? "expand" : "collapse"]();
30527     },
30528
30529     /**
30530      * Collapses the dialog to its minimized state (only the title bar is visible).
30531      * Equivalent to the user clicking the collapse dialog button.
30532      */
30533     collapse : function(){
30534         if(!this.collapsed){
30535             this.collapsed = true;
30536             this.el.addClass("x-dlg-collapsed");
30537             this.restoreHeight = this.el.getHeight();
30538             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30539         }
30540     },
30541
30542     /**
30543      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30544      * clicking the expand dialog button.
30545      */
30546     expand : function(){
30547         if(this.collapsed){
30548             this.collapsed = false;
30549             this.el.removeClass("x-dlg-collapsed");
30550             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30551         }
30552     },
30553
30554     /**
30555      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30556      * @return {Roo.TabPanel} The tabs component
30557      */
30558     initTabs : function(){
30559         var tabs = this.getTabs();
30560         while(tabs.getTab(0)){
30561             tabs.removeTab(0);
30562         }
30563         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30564             var dom = el.dom;
30565             tabs.addTab(Roo.id(dom), dom.title);
30566             dom.title = "";
30567         });
30568         tabs.activate(0);
30569         return tabs;
30570     },
30571
30572     // private
30573     beforeResize : function(){
30574         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30575     },
30576
30577     // private
30578     onResize : function(){
30579         this.refreshSize();
30580         this.syncBodyHeight();
30581         this.adjustAssets();
30582         this.focus();
30583         this.fireEvent("resize", this, this.size.width, this.size.height);
30584     },
30585
30586     // private
30587     onKeyDown : function(e){
30588         if(this.isVisible()){
30589             this.fireEvent("keydown", this, e);
30590         }
30591     },
30592
30593     /**
30594      * Resizes the dialog.
30595      * @param {Number} width
30596      * @param {Number} height
30597      * @return {Roo.BasicDialog} this
30598      */
30599     resizeTo : function(width, height){
30600         this.el.setSize(width, height);
30601         this.size = {width: width, height: height};
30602         this.syncBodyHeight();
30603         if(this.fixedcenter){
30604             this.center();
30605         }
30606         if(this.isVisible()){
30607             this.constrainXY();
30608             this.adjustAssets();
30609         }
30610         this.fireEvent("resize", this, width, height);
30611         return this;
30612     },
30613
30614
30615     /**
30616      * Resizes the dialog to fit the specified content size.
30617      * @param {Number} width
30618      * @param {Number} height
30619      * @return {Roo.BasicDialog} this
30620      */
30621     setContentSize : function(w, h){
30622         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30623         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30624         //if(!this.el.isBorderBox()){
30625             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30626             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30627         //}
30628         if(this.tabs){
30629             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30630             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30631         }
30632         this.resizeTo(w, h);
30633         return this;
30634     },
30635
30636     /**
30637      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30638      * executed in response to a particular key being pressed while the dialog is active.
30639      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30640      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30641      * @param {Function} fn The function to call
30642      * @param {Object} scope (optional) The scope of the function
30643      * @return {Roo.BasicDialog} this
30644      */
30645     addKeyListener : function(key, fn, scope){
30646         var keyCode, shift, ctrl, alt;
30647         if(typeof key == "object" && !(key instanceof Array)){
30648             keyCode = key["key"];
30649             shift = key["shift"];
30650             ctrl = key["ctrl"];
30651             alt = key["alt"];
30652         }else{
30653             keyCode = key;
30654         }
30655         var handler = function(dlg, e){
30656             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30657                 var k = e.getKey();
30658                 if(keyCode instanceof Array){
30659                     for(var i = 0, len = keyCode.length; i < len; i++){
30660                         if(keyCode[i] == k){
30661                           fn.call(scope || window, dlg, k, e);
30662                           return;
30663                         }
30664                     }
30665                 }else{
30666                     if(k == keyCode){
30667                         fn.call(scope || window, dlg, k, e);
30668                     }
30669                 }
30670             }
30671         };
30672         this.on("keydown", handler);
30673         return this;
30674     },
30675
30676     /**
30677      * Returns the TabPanel component (creates it if it doesn't exist).
30678      * Note: If you wish to simply check for the existence of tabs without creating them,
30679      * check for a null 'tabs' property.
30680      * @return {Roo.TabPanel} The tabs component
30681      */
30682     getTabs : function(){
30683         if(!this.tabs){
30684             this.el.addClass("x-dlg-auto-tabs");
30685             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30686             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30687         }
30688         return this.tabs;
30689     },
30690
30691     /**
30692      * Adds a button to the footer section of the dialog.
30693      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30694      * object or a valid Roo.DomHelper element config
30695      * @param {Function} handler The function called when the button is clicked
30696      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30697      * @return {Roo.Button} The new button
30698      */
30699     addButton : function(config, handler, scope){
30700         var dh = Roo.DomHelper;
30701         if(!this.footer){
30702             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30703         }
30704         if(!this.btnContainer){
30705             var tb = this.footer.createChild({
30706
30707                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30708                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30709             }, null, true);
30710             this.btnContainer = tb.firstChild.firstChild.firstChild;
30711         }
30712         var bconfig = {
30713             handler: handler,
30714             scope: scope,
30715             minWidth: this.minButtonWidth,
30716             hideParent:true
30717         };
30718         if(typeof config == "string"){
30719             bconfig.text = config;
30720         }else{
30721             if(config.tag){
30722                 bconfig.dhconfig = config;
30723             }else{
30724                 Roo.apply(bconfig, config);
30725             }
30726         }
30727         var fc = false;
30728         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30729             bconfig.position = Math.max(0, bconfig.position);
30730             fc = this.btnContainer.childNodes[bconfig.position];
30731         }
30732          
30733         var btn = new Roo.Button(
30734             fc ? 
30735                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30736                 : this.btnContainer.appendChild(document.createElement("td")),
30737             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30738             bconfig
30739         );
30740         this.syncBodyHeight();
30741         if(!this.buttons){
30742             /**
30743              * Array of all the buttons that have been added to this dialog via addButton
30744              * @type Array
30745              */
30746             this.buttons = [];
30747         }
30748         this.buttons.push(btn);
30749         return btn;
30750     },
30751
30752     /**
30753      * Sets the default button to be focused when the dialog is displayed.
30754      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30755      * @return {Roo.BasicDialog} this
30756      */
30757     setDefaultButton : function(btn){
30758         this.defaultButton = btn;
30759         return this;
30760     },
30761
30762     // private
30763     getHeaderFooterHeight : function(safe){
30764         var height = 0;
30765         if(this.header){
30766            height += this.header.getHeight();
30767         }
30768         if(this.footer){
30769            var fm = this.footer.getMargins();
30770             height += (this.footer.getHeight()+fm.top+fm.bottom);
30771         }
30772         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30773         height += this.centerBg.getPadding("tb");
30774         return height;
30775     },
30776
30777     // private
30778     syncBodyHeight : function()
30779     {
30780         var bd = this.body, // the text
30781             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30782             bw = this.bwrap;
30783         var height = this.size.height - this.getHeaderFooterHeight(false);
30784         bd.setHeight(height-bd.getMargins("tb"));
30785         var hh = this.header.getHeight();
30786         var h = this.size.height-hh;
30787         cb.setHeight(h);
30788         
30789         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30790         bw.setHeight(h-cb.getPadding("tb"));
30791         
30792         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30793         bd.setWidth(bw.getWidth(true));
30794         if(this.tabs){
30795             this.tabs.syncHeight();
30796             if(Roo.isIE){
30797                 this.tabs.el.repaint();
30798             }
30799         }
30800     },
30801
30802     /**
30803      * Restores the previous state of the dialog if Roo.state is configured.
30804      * @return {Roo.BasicDialog} this
30805      */
30806     restoreState : function(){
30807         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30808         if(box && box.width){
30809             this.xy = [box.x, box.y];
30810             this.resizeTo(box.width, box.height);
30811         }
30812         return this;
30813     },
30814
30815     // private
30816     beforeShow : function(){
30817         this.expand();
30818         if(this.fixedcenter){
30819             this.xy = this.el.getCenterXY(true);
30820         }
30821         if(this.modal){
30822             Roo.get(document.body).addClass("x-body-masked");
30823             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30824             this.mask.show();
30825         }
30826         this.constrainXY();
30827     },
30828
30829     // private
30830     animShow : function(){
30831         var b = Roo.get(this.animateTarget).getBox();
30832         this.proxy.setSize(b.width, b.height);
30833         this.proxy.setLocation(b.x, b.y);
30834         this.proxy.show();
30835         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30836                     true, .35, this.showEl.createDelegate(this));
30837     },
30838
30839     /**
30840      * Shows the dialog.
30841      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30842      * @return {Roo.BasicDialog} this
30843      */
30844     show : function(animateTarget){
30845         if (this.fireEvent("beforeshow", this) === false){
30846             return;
30847         }
30848         if(this.syncHeightBeforeShow){
30849             this.syncBodyHeight();
30850         }else if(this.firstShow){
30851             this.firstShow = false;
30852             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30853         }
30854         this.animateTarget = animateTarget || this.animateTarget;
30855         if(!this.el.isVisible()){
30856             this.beforeShow();
30857             if(this.animateTarget && Roo.get(this.animateTarget)){
30858                 this.animShow();
30859             }else{
30860                 this.showEl();
30861             }
30862         }
30863         return this;
30864     },
30865
30866     // private
30867     showEl : function(){
30868         this.proxy.hide();
30869         this.el.setXY(this.xy);
30870         this.el.show();
30871         this.adjustAssets(true);
30872         this.toFront();
30873         this.focus();
30874         // IE peekaboo bug - fix found by Dave Fenwick
30875         if(Roo.isIE){
30876             this.el.repaint();
30877         }
30878         this.fireEvent("show", this);
30879     },
30880
30881     /**
30882      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30883      * dialog itself will receive focus.
30884      */
30885     focus : function(){
30886         if(this.defaultButton){
30887             this.defaultButton.focus();
30888         }else{
30889             this.focusEl.focus();
30890         }
30891     },
30892
30893     // private
30894     constrainXY : function(){
30895         if(this.constraintoviewport !== false){
30896             if(!this.viewSize){
30897                 if(this.container){
30898                     var s = this.container.getSize();
30899                     this.viewSize = [s.width, s.height];
30900                 }else{
30901                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30902                 }
30903             }
30904             var s = Roo.get(this.container||document).getScroll();
30905
30906             var x = this.xy[0], y = this.xy[1];
30907             var w = this.size.width, h = this.size.height;
30908             var vw = this.viewSize[0], vh = this.viewSize[1];
30909             // only move it if it needs it
30910             var moved = false;
30911             // first validate right/bottom
30912             if(x + w > vw+s.left){
30913                 x = vw - w;
30914                 moved = true;
30915             }
30916             if(y + h > vh+s.top){
30917                 y = vh - h;
30918                 moved = true;
30919             }
30920             // then make sure top/left isn't negative
30921             if(x < s.left){
30922                 x = s.left;
30923                 moved = true;
30924             }
30925             if(y < s.top){
30926                 y = s.top;
30927                 moved = true;
30928             }
30929             if(moved){
30930                 // cache xy
30931                 this.xy = [x, y];
30932                 if(this.isVisible()){
30933                     this.el.setLocation(x, y);
30934                     this.adjustAssets();
30935                 }
30936             }
30937         }
30938     },
30939
30940     // private
30941     onDrag : function(){
30942         if(!this.proxyDrag){
30943             this.xy = this.el.getXY();
30944             this.adjustAssets();
30945         }
30946     },
30947
30948     // private
30949     adjustAssets : function(doShow){
30950         var x = this.xy[0], y = this.xy[1];
30951         var w = this.size.width, h = this.size.height;
30952         if(doShow === true){
30953             if(this.shadow){
30954                 this.shadow.show(this.el);
30955             }
30956             if(this.shim){
30957                 this.shim.show();
30958             }
30959         }
30960         if(this.shadow && this.shadow.isVisible()){
30961             this.shadow.show(this.el);
30962         }
30963         if(this.shim && this.shim.isVisible()){
30964             this.shim.setBounds(x, y, w, h);
30965         }
30966     },
30967
30968     // private
30969     adjustViewport : function(w, h){
30970         if(!w || !h){
30971             w = Roo.lib.Dom.getViewWidth();
30972             h = Roo.lib.Dom.getViewHeight();
30973         }
30974         // cache the size
30975         this.viewSize = [w, h];
30976         if(this.modal && this.mask.isVisible()){
30977             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30978             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30979         }
30980         if(this.isVisible()){
30981             this.constrainXY();
30982         }
30983     },
30984
30985     /**
30986      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30987      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30988      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30989      */
30990     destroy : function(removeEl){
30991         if(this.isVisible()){
30992             this.animateTarget = null;
30993             this.hide();
30994         }
30995         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30996         if(this.tabs){
30997             this.tabs.destroy(removeEl);
30998         }
30999         Roo.destroy(
31000              this.shim,
31001              this.proxy,
31002              this.resizer,
31003              this.close,
31004              this.mask
31005         );
31006         if(this.dd){
31007             this.dd.unreg();
31008         }
31009         if(this.buttons){
31010            for(var i = 0, len = this.buttons.length; i < len; i++){
31011                this.buttons[i].destroy();
31012            }
31013         }
31014         this.el.removeAllListeners();
31015         if(removeEl === true){
31016             this.el.update("");
31017             this.el.remove();
31018         }
31019         Roo.DialogManager.unregister(this);
31020     },
31021
31022     // private
31023     startMove : function(){
31024         if(this.proxyDrag){
31025             this.proxy.show();
31026         }
31027         if(this.constraintoviewport !== false){
31028             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31029         }
31030     },
31031
31032     // private
31033     endMove : function(){
31034         if(!this.proxyDrag){
31035             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31036         }else{
31037             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31038             this.proxy.hide();
31039         }
31040         this.refreshSize();
31041         this.adjustAssets();
31042         this.focus();
31043         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31044     },
31045
31046     /**
31047      * Brings this dialog to the front of any other visible dialogs
31048      * @return {Roo.BasicDialog} this
31049      */
31050     toFront : function(){
31051         Roo.DialogManager.bringToFront(this);
31052         return this;
31053     },
31054
31055     /**
31056      * Sends this dialog to the back (under) of any other visible dialogs
31057      * @return {Roo.BasicDialog} this
31058      */
31059     toBack : function(){
31060         Roo.DialogManager.sendToBack(this);
31061         return this;
31062     },
31063
31064     /**
31065      * Centers this dialog in the viewport
31066      * @return {Roo.BasicDialog} this
31067      */
31068     center : function(){
31069         var xy = this.el.getCenterXY(true);
31070         this.moveTo(xy[0], xy[1]);
31071         return this;
31072     },
31073
31074     /**
31075      * Moves the dialog's top-left corner to the specified point
31076      * @param {Number} x
31077      * @param {Number} y
31078      * @return {Roo.BasicDialog} this
31079      */
31080     moveTo : function(x, y){
31081         this.xy = [x,y];
31082         if(this.isVisible()){
31083             this.el.setXY(this.xy);
31084             this.adjustAssets();
31085         }
31086         return this;
31087     },
31088
31089     /**
31090      * Aligns the dialog to the specified element
31091      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31092      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31093      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31094      * @return {Roo.BasicDialog} this
31095      */
31096     alignTo : function(element, position, offsets){
31097         this.xy = this.el.getAlignToXY(element, position, offsets);
31098         if(this.isVisible()){
31099             this.el.setXY(this.xy);
31100             this.adjustAssets();
31101         }
31102         return this;
31103     },
31104
31105     /**
31106      * Anchors an element to another element and realigns it when the window is resized.
31107      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31108      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31109      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31110      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31111      * is a number, it is used as the buffer delay (defaults to 50ms).
31112      * @return {Roo.BasicDialog} this
31113      */
31114     anchorTo : function(el, alignment, offsets, monitorScroll){
31115         var action = function(){
31116             this.alignTo(el, alignment, offsets);
31117         };
31118         Roo.EventManager.onWindowResize(action, this);
31119         var tm = typeof monitorScroll;
31120         if(tm != 'undefined'){
31121             Roo.EventManager.on(window, 'scroll', action, this,
31122                 {buffer: tm == 'number' ? monitorScroll : 50});
31123         }
31124         action.call(this);
31125         return this;
31126     },
31127
31128     /**
31129      * Returns true if the dialog is visible
31130      * @return {Boolean}
31131      */
31132     isVisible : function(){
31133         return this.el.isVisible();
31134     },
31135
31136     // private
31137     animHide : function(callback){
31138         var b = Roo.get(this.animateTarget).getBox();
31139         this.proxy.show();
31140         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31141         this.el.hide();
31142         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31143                     this.hideEl.createDelegate(this, [callback]));
31144     },
31145
31146     /**
31147      * Hides the dialog.
31148      * @param {Function} callback (optional) Function to call when the dialog is hidden
31149      * @return {Roo.BasicDialog} this
31150      */
31151     hide : function(callback){
31152         if (this.fireEvent("beforehide", this) === false){
31153             return;
31154         }
31155         if(this.shadow){
31156             this.shadow.hide();
31157         }
31158         if(this.shim) {
31159           this.shim.hide();
31160         }
31161         // sometimes animateTarget seems to get set.. causing problems...
31162         // this just double checks..
31163         if(this.animateTarget && Roo.get(this.animateTarget)) {
31164            this.animHide(callback);
31165         }else{
31166             this.el.hide();
31167             this.hideEl(callback);
31168         }
31169         return this;
31170     },
31171
31172     // private
31173     hideEl : function(callback){
31174         this.proxy.hide();
31175         if(this.modal){
31176             this.mask.hide();
31177             Roo.get(document.body).removeClass("x-body-masked");
31178         }
31179         this.fireEvent("hide", this);
31180         if(typeof callback == "function"){
31181             callback();
31182         }
31183     },
31184
31185     // private
31186     hideAction : function(){
31187         this.setLeft("-10000px");
31188         this.setTop("-10000px");
31189         this.setStyle("visibility", "hidden");
31190     },
31191
31192     // private
31193     refreshSize : function(){
31194         this.size = this.el.getSize();
31195         this.xy = this.el.getXY();
31196         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31197     },
31198
31199     // private
31200     // z-index is managed by the DialogManager and may be overwritten at any time
31201     setZIndex : function(index){
31202         if(this.modal){
31203             this.mask.setStyle("z-index", index);
31204         }
31205         if(this.shim){
31206             this.shim.setStyle("z-index", ++index);
31207         }
31208         if(this.shadow){
31209             this.shadow.setZIndex(++index);
31210         }
31211         this.el.setStyle("z-index", ++index);
31212         if(this.proxy){
31213             this.proxy.setStyle("z-index", ++index);
31214         }
31215         if(this.resizer){
31216             this.resizer.proxy.setStyle("z-index", ++index);
31217         }
31218
31219         this.lastZIndex = index;
31220     },
31221
31222     /**
31223      * Returns the element for this dialog
31224      * @return {Roo.Element} The underlying dialog Element
31225      */
31226     getEl : function(){
31227         return this.el;
31228     }
31229 });
31230
31231 /**
31232  * @class Roo.DialogManager
31233  * Provides global access to BasicDialogs that have been created and
31234  * support for z-indexing (layering) multiple open dialogs.
31235  */
31236 Roo.DialogManager = function(){
31237     var list = {};
31238     var accessList = [];
31239     var front = null;
31240
31241     // private
31242     var sortDialogs = function(d1, d2){
31243         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31244     };
31245
31246     // private
31247     var orderDialogs = function(){
31248         accessList.sort(sortDialogs);
31249         var seed = Roo.DialogManager.zseed;
31250         for(var i = 0, len = accessList.length; i < len; i++){
31251             var dlg = accessList[i];
31252             if(dlg){
31253                 dlg.setZIndex(seed + (i*10));
31254             }
31255         }
31256     };
31257
31258     return {
31259         /**
31260          * The starting z-index for BasicDialogs (defaults to 9000)
31261          * @type Number The z-index value
31262          */
31263         zseed : 9000,
31264
31265         // private
31266         register : function(dlg){
31267             list[dlg.id] = dlg;
31268             accessList.push(dlg);
31269         },
31270
31271         // private
31272         unregister : function(dlg){
31273             delete list[dlg.id];
31274             var i=0;
31275             var len=0;
31276             if(!accessList.indexOf){
31277                 for(  i = 0, len = accessList.length; i < len; i++){
31278                     if(accessList[i] == dlg){
31279                         accessList.splice(i, 1);
31280                         return;
31281                     }
31282                 }
31283             }else{
31284                  i = accessList.indexOf(dlg);
31285                 if(i != -1){
31286                     accessList.splice(i, 1);
31287                 }
31288             }
31289         },
31290
31291         /**
31292          * Gets a registered dialog by id
31293          * @param {String/Object} id The id of the dialog or a dialog
31294          * @return {Roo.BasicDialog} this
31295          */
31296         get : function(id){
31297             return typeof id == "object" ? id : list[id];
31298         },
31299
31300         /**
31301          * Brings the specified dialog to the front
31302          * @param {String/Object} dlg The id of the dialog or a dialog
31303          * @return {Roo.BasicDialog} this
31304          */
31305         bringToFront : function(dlg){
31306             dlg = this.get(dlg);
31307             if(dlg != front){
31308                 front = dlg;
31309                 dlg._lastAccess = new Date().getTime();
31310                 orderDialogs();
31311             }
31312             return dlg;
31313         },
31314
31315         /**
31316          * Sends the specified dialog to the back
31317          * @param {String/Object} dlg The id of the dialog or a dialog
31318          * @return {Roo.BasicDialog} this
31319          */
31320         sendToBack : function(dlg){
31321             dlg = this.get(dlg);
31322             dlg._lastAccess = -(new Date().getTime());
31323             orderDialogs();
31324             return dlg;
31325         },
31326
31327         /**
31328          * Hides all dialogs
31329          */
31330         hideAll : function(){
31331             for(var id in list){
31332                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31333                     list[id].hide();
31334                 }
31335             }
31336         }
31337     };
31338 }();
31339
31340 /**
31341  * @class Roo.LayoutDialog
31342  * @extends Roo.BasicDialog
31343  * Dialog which provides adjustments for working with a layout in a Dialog.
31344  * Add your necessary layout config options to the dialog's config.<br>
31345  * Example usage (including a nested layout):
31346  * <pre><code>
31347 if(!dialog){
31348     dialog = new Roo.LayoutDialog("download-dlg", {
31349         modal: true,
31350         width:600,
31351         height:450,
31352         shadow:true,
31353         minWidth:500,
31354         minHeight:350,
31355         autoTabs:true,
31356         proxyDrag:true,
31357         // layout config merges with the dialog config
31358         center:{
31359             tabPosition: "top",
31360             alwaysShowTabs: true
31361         }
31362     });
31363     dialog.addKeyListener(27, dialog.hide, dialog);
31364     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31365     dialog.addButton("Build It!", this.getDownload, this);
31366
31367     // we can even add nested layouts
31368     var innerLayout = new Roo.BorderLayout("dl-inner", {
31369         east: {
31370             initialSize: 200,
31371             autoScroll:true,
31372             split:true
31373         },
31374         center: {
31375             autoScroll:true
31376         }
31377     });
31378     innerLayout.beginUpdate();
31379     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31380     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31381     innerLayout.endUpdate(true);
31382
31383     var layout = dialog.getLayout();
31384     layout.beginUpdate();
31385     layout.add("center", new Roo.ContentPanel("standard-panel",
31386                         {title: "Download the Source", fitToFrame:true}));
31387     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31388                {title: "Build your own roo.js"}));
31389     layout.getRegion("center").showPanel(sp);
31390     layout.endUpdate();
31391 }
31392 </code></pre>
31393     * @constructor
31394     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31395     * @param {Object} config configuration options
31396   */
31397 Roo.LayoutDialog = function(el, cfg){
31398     
31399     var config=  cfg;
31400     if (typeof(cfg) == 'undefined') {
31401         config = Roo.apply({}, el);
31402         // not sure why we use documentElement here.. - it should always be body.
31403         // IE7 borks horribly if we use documentElement.
31404         // webkit also does not like documentElement - it creates a body element...
31405         el = Roo.get( document.body || document.documentElement ).createChild();
31406         //config.autoCreate = true;
31407     }
31408     
31409     
31410     config.autoTabs = false;
31411     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31412     this.body.setStyle({overflow:"hidden", position:"relative"});
31413     this.layout = new Roo.BorderLayout(this.body.dom, config);
31414     this.layout.monitorWindowResize = false;
31415     this.el.addClass("x-dlg-auto-layout");
31416     // fix case when center region overwrites center function
31417     this.center = Roo.BasicDialog.prototype.center;
31418     this.on("show", this.layout.layout, this.layout, true);
31419     if (config.items) {
31420         var xitems = config.items;
31421         delete config.items;
31422         Roo.each(xitems, this.addxtype, this);
31423     }
31424     
31425     
31426 };
31427 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31428     /**
31429      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31430      * @deprecated
31431      */
31432     endUpdate : function(){
31433         this.layout.endUpdate();
31434     },
31435
31436     /**
31437      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31438      *  @deprecated
31439      */
31440     beginUpdate : function(){
31441         this.layout.beginUpdate();
31442     },
31443
31444     /**
31445      * Get the BorderLayout for this dialog
31446      * @return {Roo.BorderLayout}
31447      */
31448     getLayout : function(){
31449         return this.layout;
31450     },
31451
31452     showEl : function(){
31453         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31454         if(Roo.isIE7){
31455             this.layout.layout();
31456         }
31457     },
31458
31459     // private
31460     // Use the syncHeightBeforeShow config option to control this automatically
31461     syncBodyHeight : function(){
31462         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31463         if(this.layout){this.layout.layout();}
31464     },
31465     
31466       /**
31467      * Add an xtype element (actually adds to the layout.)
31468      * @return {Object} xdata xtype object data.
31469      */
31470     
31471     addxtype : function(c) {
31472         return this.layout.addxtype(c);
31473     }
31474 });/*
31475  * Based on:
31476  * Ext JS Library 1.1.1
31477  * Copyright(c) 2006-2007, Ext JS, LLC.
31478  *
31479  * Originally Released Under LGPL - original licence link has changed is not relivant.
31480  *
31481  * Fork - LGPL
31482  * <script type="text/javascript">
31483  */
31484  
31485 /**
31486  * @class Roo.MessageBox
31487  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31488  * Example usage:
31489  *<pre><code>
31490 // Basic alert:
31491 Roo.Msg.alert('Status', 'Changes saved successfully.');
31492
31493 // Prompt for user data:
31494 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31495     if (btn == 'ok'){
31496         // process text value...
31497     }
31498 });
31499
31500 // Show a dialog using config options:
31501 Roo.Msg.show({
31502    title:'Save Changes?',
31503    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31504    buttons: Roo.Msg.YESNOCANCEL,
31505    fn: processResult,
31506    animEl: 'elId'
31507 });
31508 </code></pre>
31509  * @singleton
31510  */
31511 Roo.MessageBox = function(){
31512     var dlg, opt, mask, waitTimer;
31513     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31514     var buttons, activeTextEl, bwidth;
31515
31516     // private
31517     var handleButton = function(button){
31518         dlg.hide();
31519         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31520     };
31521
31522     // private
31523     var handleHide = function(){
31524         if(opt && opt.cls){
31525             dlg.el.removeClass(opt.cls);
31526         }
31527         if(waitTimer){
31528             Roo.TaskMgr.stop(waitTimer);
31529             waitTimer = null;
31530         }
31531     };
31532
31533     // private
31534     var updateButtons = function(b){
31535         var width = 0;
31536         if(!b){
31537             buttons["ok"].hide();
31538             buttons["cancel"].hide();
31539             buttons["yes"].hide();
31540             buttons["no"].hide();
31541             dlg.footer.dom.style.display = 'none';
31542             return width;
31543         }
31544         dlg.footer.dom.style.display = '';
31545         for(var k in buttons){
31546             if(typeof buttons[k] != "function"){
31547                 if(b[k]){
31548                     buttons[k].show();
31549                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31550                     width += buttons[k].el.getWidth()+15;
31551                 }else{
31552                     buttons[k].hide();
31553                 }
31554             }
31555         }
31556         return width;
31557     };
31558
31559     // private
31560     var handleEsc = function(d, k, e){
31561         if(opt && opt.closable !== false){
31562             dlg.hide();
31563         }
31564         if(e){
31565             e.stopEvent();
31566         }
31567     };
31568
31569     return {
31570         /**
31571          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31572          * @return {Roo.BasicDialog} The BasicDialog element
31573          */
31574         getDialog : function(){
31575            if(!dlg){
31576                 dlg = new Roo.BasicDialog("x-msg-box", {
31577                     autoCreate : true,
31578                     shadow: true,
31579                     draggable: true,
31580                     resizable:false,
31581                     constraintoviewport:false,
31582                     fixedcenter:true,
31583                     collapsible : false,
31584                     shim:true,
31585                     modal: true,
31586                     width:400, height:100,
31587                     buttonAlign:"center",
31588                     closeClick : function(){
31589                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31590                             handleButton("no");
31591                         }else{
31592                             handleButton("cancel");
31593                         }
31594                     }
31595                 });
31596                 dlg.on("hide", handleHide);
31597                 mask = dlg.mask;
31598                 dlg.addKeyListener(27, handleEsc);
31599                 buttons = {};
31600                 var bt = this.buttonText;
31601                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31602                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31603                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31604                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31605                 bodyEl = dlg.body.createChild({
31606
31607                     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>'
31608                 });
31609                 msgEl = bodyEl.dom.firstChild;
31610                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31611                 textboxEl.enableDisplayMode();
31612                 textboxEl.addKeyListener([10,13], function(){
31613                     if(dlg.isVisible() && opt && opt.buttons){
31614                         if(opt.buttons.ok){
31615                             handleButton("ok");
31616                         }else if(opt.buttons.yes){
31617                             handleButton("yes");
31618                         }
31619                     }
31620                 });
31621                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31622                 textareaEl.enableDisplayMode();
31623                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31624                 progressEl.enableDisplayMode();
31625                 var pf = progressEl.dom.firstChild;
31626                 if (pf) {
31627                     pp = Roo.get(pf.firstChild);
31628                     pp.setHeight(pf.offsetHeight);
31629                 }
31630                 
31631             }
31632             return dlg;
31633         },
31634
31635         /**
31636          * Updates the message box body text
31637          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31638          * the XHTML-compliant non-breaking space character '&amp;#160;')
31639          * @return {Roo.MessageBox} This message box
31640          */
31641         updateText : function(text){
31642             if(!dlg.isVisible() && !opt.width){
31643                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31644             }
31645             msgEl.innerHTML = text || '&#160;';
31646       
31647             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31648             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31649             var w = Math.max(
31650                     Math.min(opt.width || cw , this.maxWidth), 
31651                     Math.max(opt.minWidth || this.minWidth, bwidth)
31652             );
31653             if(opt.prompt){
31654                 activeTextEl.setWidth(w);
31655             }
31656             if(dlg.isVisible()){
31657                 dlg.fixedcenter = false;
31658             }
31659             // to big, make it scroll. = But as usual stupid IE does not support
31660             // !important..
31661             
31662             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31663                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31664                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31665             } else {
31666                 bodyEl.dom.style.height = '';
31667                 bodyEl.dom.style.overflowY = '';
31668             }
31669             if (cw > w) {
31670                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31671             } else {
31672                 bodyEl.dom.style.overflowX = '';
31673             }
31674             
31675             dlg.setContentSize(w, bodyEl.getHeight());
31676             if(dlg.isVisible()){
31677                 dlg.fixedcenter = true;
31678             }
31679             return this;
31680         },
31681
31682         /**
31683          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31684          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31685          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31686          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31687          * @return {Roo.MessageBox} This message box
31688          */
31689         updateProgress : function(value, text){
31690             if(text){
31691                 this.updateText(text);
31692             }
31693             if (pp) { // weird bug on my firefox - for some reason this is not defined
31694                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31695             }
31696             return this;
31697         },        
31698
31699         /**
31700          * Returns true if the message box is currently displayed
31701          * @return {Boolean} True if the message box is visible, else false
31702          */
31703         isVisible : function(){
31704             return dlg && dlg.isVisible();  
31705         },
31706
31707         /**
31708          * Hides the message box if it is displayed
31709          */
31710         hide : function(){
31711             if(this.isVisible()){
31712                 dlg.hide();
31713             }  
31714         },
31715
31716         /**
31717          * Displays a new message box, or reinitializes an existing message box, based on the config options
31718          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31719          * The following config object properties are supported:
31720          * <pre>
31721 Property    Type             Description
31722 ----------  ---------------  ------------------------------------------------------------------------------------
31723 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31724                                    closes (defaults to undefined)
31725 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31726                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31727 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31728                                    progress and wait dialogs will ignore this property and always hide the
31729                                    close button as they can only be closed programmatically.
31730 cls               String           A custom CSS class to apply to the message box element
31731 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31732                                    displayed (defaults to 75)
31733 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31734                                    function will be btn (the name of the button that was clicked, if applicable,
31735                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31736                                    Progress and wait dialogs will ignore this option since they do not respond to
31737                                    user actions and can only be closed programmatically, so any required function
31738                                    should be called by the same code after it closes the dialog.
31739 icon              String           A CSS class that provides a background image to be used as an icon for
31740                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31741 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31742 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31743 modal             Boolean          False to allow user interaction with the page while the message box is
31744                                    displayed (defaults to true)
31745 msg               String           A string that will replace the existing message box body text (defaults
31746                                    to the XHTML-compliant non-breaking space character '&#160;')
31747 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31748 progress          Boolean          True to display a progress bar (defaults to false)
31749 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31750 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31751 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31752 title             String           The title text
31753 value             String           The string value to set into the active textbox element if displayed
31754 wait              Boolean          True to display a progress bar (defaults to false)
31755 width             Number           The width of the dialog in pixels
31756 </pre>
31757          *
31758          * Example usage:
31759          * <pre><code>
31760 Roo.Msg.show({
31761    title: 'Address',
31762    msg: 'Please enter your address:',
31763    width: 300,
31764    buttons: Roo.MessageBox.OKCANCEL,
31765    multiline: true,
31766    fn: saveAddress,
31767    animEl: 'addAddressBtn'
31768 });
31769 </code></pre>
31770          * @param {Object} config Configuration options
31771          * @return {Roo.MessageBox} This message box
31772          */
31773         show : function(options)
31774         {
31775             
31776             // this causes nightmares if you show one dialog after another
31777             // especially on callbacks..
31778              
31779             if(this.isVisible()){
31780                 
31781                 this.hide();
31782                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31783                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31784                 Roo.log("New Dialog Message:" +  options.msg )
31785                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31786                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31787                 
31788             }
31789             var d = this.getDialog();
31790             opt = options;
31791             d.setTitle(opt.title || "&#160;");
31792             d.close.setDisplayed(opt.closable !== false);
31793             activeTextEl = textboxEl;
31794             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31795             if(opt.prompt){
31796                 if(opt.multiline){
31797                     textboxEl.hide();
31798                     textareaEl.show();
31799                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31800                         opt.multiline : this.defaultTextHeight);
31801                     activeTextEl = textareaEl;
31802                 }else{
31803                     textboxEl.show();
31804                     textareaEl.hide();
31805                 }
31806             }else{
31807                 textboxEl.hide();
31808                 textareaEl.hide();
31809             }
31810             progressEl.setDisplayed(opt.progress === true);
31811             this.updateProgress(0);
31812             activeTextEl.dom.value = opt.value || "";
31813             if(opt.prompt){
31814                 dlg.setDefaultButton(activeTextEl);
31815             }else{
31816                 var bs = opt.buttons;
31817                 var db = null;
31818                 if(bs && bs.ok){
31819                     db = buttons["ok"];
31820                 }else if(bs && bs.yes){
31821                     db = buttons["yes"];
31822                 }
31823                 dlg.setDefaultButton(db);
31824             }
31825             bwidth = updateButtons(opt.buttons);
31826             this.updateText(opt.msg);
31827             if(opt.cls){
31828                 d.el.addClass(opt.cls);
31829             }
31830             d.proxyDrag = opt.proxyDrag === true;
31831             d.modal = opt.modal !== false;
31832             d.mask = opt.modal !== false ? mask : false;
31833             if(!d.isVisible()){
31834                 // force it to the end of the z-index stack so it gets a cursor in FF
31835                 document.body.appendChild(dlg.el.dom);
31836                 d.animateTarget = null;
31837                 d.show(options.animEl);
31838             }
31839             return this;
31840         },
31841
31842         /**
31843          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31844          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31845          * and closing the message box when the process is complete.
31846          * @param {String} title The title bar text
31847          * @param {String} msg The message box body text
31848          * @return {Roo.MessageBox} This message box
31849          */
31850         progress : function(title, msg){
31851             this.show({
31852                 title : title,
31853                 msg : msg,
31854                 buttons: false,
31855                 progress:true,
31856                 closable:false,
31857                 minWidth: this.minProgressWidth,
31858                 modal : true
31859             });
31860             return this;
31861         },
31862
31863         /**
31864          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31865          * If a callback function is passed it will be called after the user clicks the button, and the
31866          * id of the button that was clicked will be passed as the only parameter to the callback
31867          * (could also be the top-right close button).
31868          * @param {String} title The title bar text
31869          * @param {String} msg The message box body text
31870          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31871          * @param {Object} scope (optional) The scope of the callback function
31872          * @return {Roo.MessageBox} This message box
31873          */
31874         alert : function(title, msg, fn, scope){
31875             this.show({
31876                 title : title,
31877                 msg : msg,
31878                 buttons: this.OK,
31879                 fn: fn,
31880                 scope : scope,
31881                 modal : true
31882             });
31883             return this;
31884         },
31885
31886         /**
31887          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31888          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31889          * You are responsible for closing the message box when the process is complete.
31890          * @param {String} msg The message box body text
31891          * @param {String} title (optional) The title bar text
31892          * @return {Roo.MessageBox} This message box
31893          */
31894         wait : function(msg, title){
31895             this.show({
31896                 title : title,
31897                 msg : msg,
31898                 buttons: false,
31899                 closable:false,
31900                 progress:true,
31901                 modal:true,
31902                 width:300,
31903                 wait:true
31904             });
31905             waitTimer = Roo.TaskMgr.start({
31906                 run: function(i){
31907                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31908                 },
31909                 interval: 1000
31910             });
31911             return this;
31912         },
31913
31914         /**
31915          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31916          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31917          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31918          * @param {String} title The title bar text
31919          * @param {String} msg The message box body text
31920          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31921          * @param {Object} scope (optional) The scope of the callback function
31922          * @return {Roo.MessageBox} This message box
31923          */
31924         confirm : function(title, msg, fn, scope){
31925             this.show({
31926                 title : title,
31927                 msg : msg,
31928                 buttons: this.YESNO,
31929                 fn: fn,
31930                 scope : scope,
31931                 modal : true
31932             });
31933             return this;
31934         },
31935
31936         /**
31937          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31938          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31939          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31940          * (could also be the top-right close button) and the text that was entered will be passed as the two
31941          * parameters to the callback.
31942          * @param {String} title The title bar text
31943          * @param {String} msg The message box body text
31944          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31945          * @param {Object} scope (optional) The scope of the callback function
31946          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31947          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31948          * @return {Roo.MessageBox} This message box
31949          */
31950         prompt : function(title, msg, fn, scope, multiline){
31951             this.show({
31952                 title : title,
31953                 msg : msg,
31954                 buttons: this.OKCANCEL,
31955                 fn: fn,
31956                 minWidth:250,
31957                 scope : scope,
31958                 prompt:true,
31959                 multiline: multiline,
31960                 modal : true
31961             });
31962             return this;
31963         },
31964
31965         /**
31966          * Button config that displays a single OK button
31967          * @type Object
31968          */
31969         OK : {ok:true},
31970         /**
31971          * Button config that displays Yes and No buttons
31972          * @type Object
31973          */
31974         YESNO : {yes:true, no:true},
31975         /**
31976          * Button config that displays OK and Cancel buttons
31977          * @type Object
31978          */
31979         OKCANCEL : {ok:true, cancel:true},
31980         /**
31981          * Button config that displays Yes, No and Cancel buttons
31982          * @type Object
31983          */
31984         YESNOCANCEL : {yes:true, no:true, cancel:true},
31985
31986         /**
31987          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31988          * @type Number
31989          */
31990         defaultTextHeight : 75,
31991         /**
31992          * The maximum width in pixels of the message box (defaults to 600)
31993          * @type Number
31994          */
31995         maxWidth : 600,
31996         /**
31997          * The minimum width in pixels of the message box (defaults to 100)
31998          * @type Number
31999          */
32000         minWidth : 100,
32001         /**
32002          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
32003          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
32004          * @type Number
32005          */
32006         minProgressWidth : 250,
32007         /**
32008          * An object containing the default button text strings that can be overriden for localized language support.
32009          * Supported properties are: ok, cancel, yes and no.
32010          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
32011          * @type Object
32012          */
32013         buttonText : {
32014             ok : "OK",
32015             cancel : "Cancel",
32016             yes : "Yes",
32017             no : "No"
32018         }
32019     };
32020 }();
32021
32022 /**
32023  * Shorthand for {@link Roo.MessageBox}
32024  */
32025 Roo.Msg = Roo.MessageBox;/*
32026  * Based on:
32027  * Ext JS Library 1.1.1
32028  * Copyright(c) 2006-2007, Ext JS, LLC.
32029  *
32030  * Originally Released Under LGPL - original licence link has changed is not relivant.
32031  *
32032  * Fork - LGPL
32033  * <script type="text/javascript">
32034  */
32035 /**
32036  * @class Roo.QuickTips
32037  * Provides attractive and customizable tooltips for any element.
32038  * @singleton
32039  */
32040 Roo.QuickTips = function(){
32041     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32042     var ce, bd, xy, dd;
32043     var visible = false, disabled = true, inited = false;
32044     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32045     
32046     var onOver = function(e){
32047         if(disabled){
32048             return;
32049         }
32050         var t = e.getTarget();
32051         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32052             return;
32053         }
32054         if(ce && t == ce.el){
32055             clearTimeout(hideProc);
32056             return;
32057         }
32058         if(t && tagEls[t.id]){
32059             tagEls[t.id].el = t;
32060             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32061             return;
32062         }
32063         var ttp, et = Roo.fly(t);
32064         var ns = cfg.namespace;
32065         if(tm.interceptTitles && t.title){
32066             ttp = t.title;
32067             t.qtip = ttp;
32068             t.removeAttribute("title");
32069             e.preventDefault();
32070         }else{
32071             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
32072         }
32073         if(ttp){
32074             showProc = show.defer(tm.showDelay, tm, [{
32075                 el: t, 
32076                 text: ttp, 
32077                 width: et.getAttributeNS(ns, cfg.width),
32078                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32079                 title: et.getAttributeNS(ns, cfg.title),
32080                     cls: et.getAttributeNS(ns, cfg.cls)
32081             }]);
32082         }
32083     };
32084     
32085     var onOut = function(e){
32086         clearTimeout(showProc);
32087         var t = e.getTarget();
32088         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32089             hideProc = setTimeout(hide, tm.hideDelay);
32090         }
32091     };
32092     
32093     var onMove = function(e){
32094         if(disabled){
32095             return;
32096         }
32097         xy = e.getXY();
32098         xy[1] += 18;
32099         if(tm.trackMouse && ce){
32100             el.setXY(xy);
32101         }
32102     };
32103     
32104     var onDown = function(e){
32105         clearTimeout(showProc);
32106         clearTimeout(hideProc);
32107         if(!e.within(el)){
32108             if(tm.hideOnClick){
32109                 hide();
32110                 tm.disable();
32111                 tm.enable.defer(100, tm);
32112             }
32113         }
32114     };
32115     
32116     var getPad = function(){
32117         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32118     };
32119
32120     var show = function(o){
32121         if(disabled){
32122             return;
32123         }
32124         clearTimeout(dismissProc);
32125         ce = o;
32126         if(removeCls){ // in case manually hidden
32127             el.removeClass(removeCls);
32128             removeCls = null;
32129         }
32130         if(ce.cls){
32131             el.addClass(ce.cls);
32132             removeCls = ce.cls;
32133         }
32134         if(ce.title){
32135             tipTitle.update(ce.title);
32136             tipTitle.show();
32137         }else{
32138             tipTitle.update('');
32139             tipTitle.hide();
32140         }
32141         el.dom.style.width  = tm.maxWidth+'px';
32142         //tipBody.dom.style.width = '';
32143         tipBodyText.update(o.text);
32144         var p = getPad(), w = ce.width;
32145         if(!w){
32146             var td = tipBodyText.dom;
32147             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32148             if(aw > tm.maxWidth){
32149                 w = tm.maxWidth;
32150             }else if(aw < tm.minWidth){
32151                 w = tm.minWidth;
32152             }else{
32153                 w = aw;
32154             }
32155         }
32156         //tipBody.setWidth(w);
32157         el.setWidth(parseInt(w, 10) + p);
32158         if(ce.autoHide === false){
32159             close.setDisplayed(true);
32160             if(dd){
32161                 dd.unlock();
32162             }
32163         }else{
32164             close.setDisplayed(false);
32165             if(dd){
32166                 dd.lock();
32167             }
32168         }
32169         if(xy){
32170             el.avoidY = xy[1]-18;
32171             el.setXY(xy);
32172         }
32173         if(tm.animate){
32174             el.setOpacity(.1);
32175             el.setStyle("visibility", "visible");
32176             el.fadeIn({callback: afterShow});
32177         }else{
32178             afterShow();
32179         }
32180     };
32181     
32182     var afterShow = function(){
32183         if(ce){
32184             el.show();
32185             esc.enable();
32186             if(tm.autoDismiss && ce.autoHide !== false){
32187                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32188             }
32189         }
32190     };
32191     
32192     var hide = function(noanim){
32193         clearTimeout(dismissProc);
32194         clearTimeout(hideProc);
32195         ce = null;
32196         if(el.isVisible()){
32197             esc.disable();
32198             if(noanim !== true && tm.animate){
32199                 el.fadeOut({callback: afterHide});
32200             }else{
32201                 afterHide();
32202             } 
32203         }
32204     };
32205     
32206     var afterHide = function(){
32207         el.hide();
32208         if(removeCls){
32209             el.removeClass(removeCls);
32210             removeCls = null;
32211         }
32212     };
32213     
32214     return {
32215         /**
32216         * @cfg {Number} minWidth
32217         * The minimum width of the quick tip (defaults to 40)
32218         */
32219        minWidth : 40,
32220         /**
32221         * @cfg {Number} maxWidth
32222         * The maximum width of the quick tip (defaults to 300)
32223         */
32224        maxWidth : 300,
32225         /**
32226         * @cfg {Boolean} interceptTitles
32227         * True to automatically use the element's DOM title value if available (defaults to false)
32228         */
32229        interceptTitles : false,
32230         /**
32231         * @cfg {Boolean} trackMouse
32232         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32233         */
32234        trackMouse : false,
32235         /**
32236         * @cfg {Boolean} hideOnClick
32237         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32238         */
32239        hideOnClick : true,
32240         /**
32241         * @cfg {Number} showDelay
32242         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32243         */
32244        showDelay : 500,
32245         /**
32246         * @cfg {Number} hideDelay
32247         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32248         */
32249        hideDelay : 200,
32250         /**
32251         * @cfg {Boolean} autoHide
32252         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32253         * Used in conjunction with hideDelay.
32254         */
32255        autoHide : true,
32256         /**
32257         * @cfg {Boolean}
32258         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32259         * (defaults to true).  Used in conjunction with autoDismissDelay.
32260         */
32261        autoDismiss : true,
32262         /**
32263         * @cfg {Number}
32264         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32265         */
32266        autoDismissDelay : 5000,
32267        /**
32268         * @cfg {Boolean} animate
32269         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32270         */
32271        animate : false,
32272
32273        /**
32274         * @cfg {String} title
32275         * Title text to display (defaults to '').  This can be any valid HTML markup.
32276         */
32277         title: '',
32278        /**
32279         * @cfg {String} text
32280         * Body text to display (defaults to '').  This can be any valid HTML markup.
32281         */
32282         text : '',
32283        /**
32284         * @cfg {String} cls
32285         * A CSS class to apply to the base quick tip element (defaults to '').
32286         */
32287         cls : '',
32288        /**
32289         * @cfg {Number} width
32290         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32291         * minWidth or maxWidth.
32292         */
32293         width : null,
32294
32295     /**
32296      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32297      * or display QuickTips in a page.
32298      */
32299        init : function(){
32300           tm = Roo.QuickTips;
32301           cfg = tm.tagConfig;
32302           if(!inited){
32303               if(!Roo.isReady){ // allow calling of init() before onReady
32304                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32305                   return;
32306               }
32307               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32308               el.fxDefaults = {stopFx: true};
32309               // maximum custom styling
32310               //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>');
32311               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>');              
32312               tipTitle = el.child('h3');
32313               tipTitle.enableDisplayMode("block");
32314               tipBody = el.child('div.x-tip-bd');
32315               tipBodyText = el.child('div.x-tip-bd-inner');
32316               //bdLeft = el.child('div.x-tip-bd-left');
32317               //bdRight = el.child('div.x-tip-bd-right');
32318               close = el.child('div.x-tip-close');
32319               close.enableDisplayMode("block");
32320               close.on("click", hide);
32321               var d = Roo.get(document);
32322               d.on("mousedown", onDown);
32323               d.on("mouseover", onOver);
32324               d.on("mouseout", onOut);
32325               d.on("mousemove", onMove);
32326               esc = d.addKeyListener(27, hide);
32327               esc.disable();
32328               if(Roo.dd.DD){
32329                   dd = el.initDD("default", null, {
32330                       onDrag : function(){
32331                           el.sync();  
32332                       }
32333                   });
32334                   dd.setHandleElId(tipTitle.id);
32335                   dd.lock();
32336               }
32337               inited = true;
32338           }
32339           this.enable(); 
32340        },
32341
32342     /**
32343      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32344      * are supported:
32345      * <pre>
32346 Property    Type                   Description
32347 ----------  ---------------------  ------------------------------------------------------------------------
32348 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32349      * </ul>
32350      * @param {Object} config The config object
32351      */
32352        register : function(config){
32353            var cs = config instanceof Array ? config : arguments;
32354            for(var i = 0, len = cs.length; i < len; i++) {
32355                var c = cs[i];
32356                var target = c.target;
32357                if(target){
32358                    if(target instanceof Array){
32359                        for(var j = 0, jlen = target.length; j < jlen; j++){
32360                            tagEls[target[j]] = c;
32361                        }
32362                    }else{
32363                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32364                    }
32365                }
32366            }
32367        },
32368
32369     /**
32370      * Removes this quick tip from its element and destroys it.
32371      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32372      */
32373        unregister : function(el){
32374            delete tagEls[Roo.id(el)];
32375        },
32376
32377     /**
32378      * Enable this quick tip.
32379      */
32380        enable : function(){
32381            if(inited && disabled){
32382                locks.pop();
32383                if(locks.length < 1){
32384                    disabled = false;
32385                }
32386            }
32387        },
32388
32389     /**
32390      * Disable this quick tip.
32391      */
32392        disable : function(){
32393           disabled = true;
32394           clearTimeout(showProc);
32395           clearTimeout(hideProc);
32396           clearTimeout(dismissProc);
32397           if(ce){
32398               hide(true);
32399           }
32400           locks.push(1);
32401        },
32402
32403     /**
32404      * Returns true if the quick tip is enabled, else false.
32405      */
32406        isEnabled : function(){
32407             return !disabled;
32408        },
32409
32410         // private
32411        tagConfig : {
32412            namespace : "roo", // was ext?? this may break..
32413            alt_namespace : "ext",
32414            attribute : "qtip",
32415            width : "width",
32416            target : "target",
32417            title : "qtitle",
32418            hide : "hide",
32419            cls : "qclass"
32420        }
32421    };
32422 }();
32423
32424 // backwards compat
32425 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32426  * Based on:
32427  * Ext JS Library 1.1.1
32428  * Copyright(c) 2006-2007, Ext JS, LLC.
32429  *
32430  * Originally Released Under LGPL - original licence link has changed is not relivant.
32431  *
32432  * Fork - LGPL
32433  * <script type="text/javascript">
32434  */
32435  
32436
32437 /**
32438  * @class Roo.tree.TreePanel
32439  * @extends Roo.data.Tree
32440
32441  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32442  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32443  * @cfg {Boolean} enableDD true to enable drag and drop
32444  * @cfg {Boolean} enableDrag true to enable just drag
32445  * @cfg {Boolean} enableDrop true to enable just drop
32446  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32447  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32448  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32449  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32450  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32451  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32452  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32453  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32454  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32455  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32456  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32457  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32458  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32459  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32460  * @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>
32461  * @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>
32462  * 
32463  * @constructor
32464  * @param {String/HTMLElement/Element} el The container element
32465  * @param {Object} config
32466  */
32467 Roo.tree.TreePanel = function(el, config){
32468     var root = false;
32469     var loader = false;
32470     if (config.root) {
32471         root = config.root;
32472         delete config.root;
32473     }
32474     if (config.loader) {
32475         loader = config.loader;
32476         delete config.loader;
32477     }
32478     
32479     Roo.apply(this, config);
32480     Roo.tree.TreePanel.superclass.constructor.call(this);
32481     this.el = Roo.get(el);
32482     this.el.addClass('x-tree');
32483     //console.log(root);
32484     if (root) {
32485         this.setRootNode( Roo.factory(root, Roo.tree));
32486     }
32487     if (loader) {
32488         this.loader = Roo.factory(loader, Roo.tree);
32489     }
32490    /**
32491     * Read-only. The id of the container element becomes this TreePanel's id.
32492     */
32493     this.id = this.el.id;
32494     this.addEvents({
32495         /**
32496         * @event beforeload
32497         * Fires before a node is loaded, return false to cancel
32498         * @param {Node} node The node being loaded
32499         */
32500         "beforeload" : true,
32501         /**
32502         * @event load
32503         * Fires when a node is loaded
32504         * @param {Node} node The node that was loaded
32505         */
32506         "load" : true,
32507         /**
32508         * @event textchange
32509         * Fires when the text for a node is changed
32510         * @param {Node} node The node
32511         * @param {String} text The new text
32512         * @param {String} oldText The old text
32513         */
32514         "textchange" : true,
32515         /**
32516         * @event beforeexpand
32517         * Fires before a node is expanded, return false to cancel.
32518         * @param {Node} node The node
32519         * @param {Boolean} deep
32520         * @param {Boolean} anim
32521         */
32522         "beforeexpand" : true,
32523         /**
32524         * @event beforecollapse
32525         * Fires before a node is collapsed, return false to cancel.
32526         * @param {Node} node The node
32527         * @param {Boolean} deep
32528         * @param {Boolean} anim
32529         */
32530         "beforecollapse" : true,
32531         /**
32532         * @event expand
32533         * Fires when a node is expanded
32534         * @param {Node} node The node
32535         */
32536         "expand" : true,
32537         /**
32538         * @event disabledchange
32539         * Fires when the disabled status of a node changes
32540         * @param {Node} node The node
32541         * @param {Boolean} disabled
32542         */
32543         "disabledchange" : true,
32544         /**
32545         * @event collapse
32546         * Fires when a node is collapsed
32547         * @param {Node} node The node
32548         */
32549         "collapse" : true,
32550         /**
32551         * @event beforeclick
32552         * Fires before click processing on a node. Return false to cancel the default action.
32553         * @param {Node} node The node
32554         * @param {Roo.EventObject} e The event object
32555         */
32556         "beforeclick":true,
32557         /**
32558         * @event checkchange
32559         * Fires when a node with a checkbox's checked property changes
32560         * @param {Node} this This node
32561         * @param {Boolean} checked
32562         */
32563         "checkchange":true,
32564         /**
32565         * @event click
32566         * Fires when a node is clicked
32567         * @param {Node} node The node
32568         * @param {Roo.EventObject} e The event object
32569         */
32570         "click":true,
32571         /**
32572         * @event dblclick
32573         * Fires when a node is double clicked
32574         * @param {Node} node The node
32575         * @param {Roo.EventObject} e The event object
32576         */
32577         "dblclick":true,
32578         /**
32579         * @event contextmenu
32580         * Fires when a node is right clicked
32581         * @param {Node} node The node
32582         * @param {Roo.EventObject} e The event object
32583         */
32584         "contextmenu":true,
32585         /**
32586         * @event beforechildrenrendered
32587         * Fires right before the child nodes for a node are rendered
32588         * @param {Node} node The node
32589         */
32590         "beforechildrenrendered":true,
32591         /**
32592         * @event startdrag
32593         * Fires when a node starts being dragged
32594         * @param {Roo.tree.TreePanel} this
32595         * @param {Roo.tree.TreeNode} node
32596         * @param {event} e The raw browser event
32597         */ 
32598        "startdrag" : true,
32599        /**
32600         * @event enddrag
32601         * Fires when a drag operation is complete
32602         * @param {Roo.tree.TreePanel} this
32603         * @param {Roo.tree.TreeNode} node
32604         * @param {event} e The raw browser event
32605         */
32606        "enddrag" : true,
32607        /**
32608         * @event dragdrop
32609         * Fires when a dragged node is dropped on a valid DD target
32610         * @param {Roo.tree.TreePanel} this
32611         * @param {Roo.tree.TreeNode} node
32612         * @param {DD} dd The dd it was dropped on
32613         * @param {event} e The raw browser event
32614         */
32615        "dragdrop" : true,
32616        /**
32617         * @event beforenodedrop
32618         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32619         * passed to handlers has the following properties:<br />
32620         * <ul style="padding:5px;padding-left:16px;">
32621         * <li>tree - The TreePanel</li>
32622         * <li>target - The node being targeted for the drop</li>
32623         * <li>data - The drag data from the drag source</li>
32624         * <li>point - The point of the drop - append, above or below</li>
32625         * <li>source - The drag source</li>
32626         * <li>rawEvent - Raw mouse event</li>
32627         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32628         * to be inserted by setting them on this object.</li>
32629         * <li>cancel - Set this to true to cancel the drop.</li>
32630         * </ul>
32631         * @param {Object} dropEvent
32632         */
32633        "beforenodedrop" : true,
32634        /**
32635         * @event nodedrop
32636         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32637         * passed to handlers has the following properties:<br />
32638         * <ul style="padding:5px;padding-left:16px;">
32639         * <li>tree - The TreePanel</li>
32640         * <li>target - The node being targeted for the drop</li>
32641         * <li>data - The drag data from the drag source</li>
32642         * <li>point - The point of the drop - append, above or below</li>
32643         * <li>source - The drag source</li>
32644         * <li>rawEvent - Raw mouse event</li>
32645         * <li>dropNode - Dropped node(s).</li>
32646         * </ul>
32647         * @param {Object} dropEvent
32648         */
32649        "nodedrop" : true,
32650         /**
32651         * @event nodedragover
32652         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32653         * passed to handlers has the following properties:<br />
32654         * <ul style="padding:5px;padding-left:16px;">
32655         * <li>tree - The TreePanel</li>
32656         * <li>target - The node being targeted for the drop</li>
32657         * <li>data - The drag data from the drag source</li>
32658         * <li>point - The point of the drop - append, above or below</li>
32659         * <li>source - The drag source</li>
32660         * <li>rawEvent - Raw mouse event</li>
32661         * <li>dropNode - Drop node(s) provided by the source.</li>
32662         * <li>cancel - Set this to true to signal drop not allowed.</li>
32663         * </ul>
32664         * @param {Object} dragOverEvent
32665         */
32666        "nodedragover" : true
32667         
32668     });
32669     if(this.singleExpand){
32670        this.on("beforeexpand", this.restrictExpand, this);
32671     }
32672     if (this.editor) {
32673         this.editor.tree = this;
32674         this.editor = Roo.factory(this.editor, Roo.tree);
32675     }
32676     
32677     if (this.selModel) {
32678         this.selModel = Roo.factory(this.selModel, Roo.tree);
32679     }
32680    
32681 };
32682 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32683     rootVisible : true,
32684     animate: Roo.enableFx,
32685     lines : true,
32686     enableDD : false,
32687     hlDrop : Roo.enableFx,
32688   
32689     renderer: false,
32690     
32691     rendererTip: false,
32692     // private
32693     restrictExpand : function(node){
32694         var p = node.parentNode;
32695         if(p){
32696             if(p.expandedChild && p.expandedChild.parentNode == p){
32697                 p.expandedChild.collapse();
32698             }
32699             p.expandedChild = node;
32700         }
32701     },
32702
32703     // private override
32704     setRootNode : function(node){
32705         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32706         if(!this.rootVisible){
32707             node.ui = new Roo.tree.RootTreeNodeUI(node);
32708         }
32709         return node;
32710     },
32711
32712     /**
32713      * Returns the container element for this TreePanel
32714      */
32715     getEl : function(){
32716         return this.el;
32717     },
32718
32719     /**
32720      * Returns the default TreeLoader for this TreePanel
32721      */
32722     getLoader : function(){
32723         return this.loader;
32724     },
32725
32726     /**
32727      * Expand all nodes
32728      */
32729     expandAll : function(){
32730         this.root.expand(true);
32731     },
32732
32733     /**
32734      * Collapse all nodes
32735      */
32736     collapseAll : function(){
32737         this.root.collapse(true);
32738     },
32739
32740     /**
32741      * Returns the selection model used by this TreePanel
32742      */
32743     getSelectionModel : function(){
32744         if(!this.selModel){
32745             this.selModel = new Roo.tree.DefaultSelectionModel();
32746         }
32747         return this.selModel;
32748     },
32749
32750     /**
32751      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32752      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32753      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32754      * @return {Array}
32755      */
32756     getChecked : function(a, startNode){
32757         startNode = startNode || this.root;
32758         var r = [];
32759         var f = function(){
32760             if(this.attributes.checked){
32761                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32762             }
32763         }
32764         startNode.cascade(f);
32765         return r;
32766     },
32767
32768     /**
32769      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32770      * @param {String} path
32771      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32772      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32773      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32774      */
32775     expandPath : function(path, attr, callback){
32776         attr = attr || "id";
32777         var keys = path.split(this.pathSeparator);
32778         var curNode = this.root;
32779         if(curNode.attributes[attr] != keys[1]){ // invalid root
32780             if(callback){
32781                 callback(false, null);
32782             }
32783             return;
32784         }
32785         var index = 1;
32786         var f = function(){
32787             if(++index == keys.length){
32788                 if(callback){
32789                     callback(true, curNode);
32790                 }
32791                 return;
32792             }
32793             var c = curNode.findChild(attr, keys[index]);
32794             if(!c){
32795                 if(callback){
32796                     callback(false, curNode);
32797                 }
32798                 return;
32799             }
32800             curNode = c;
32801             c.expand(false, false, f);
32802         };
32803         curNode.expand(false, false, f);
32804     },
32805
32806     /**
32807      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32808      * @param {String} path
32809      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32810      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32811      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32812      */
32813     selectPath : function(path, attr, callback){
32814         attr = attr || "id";
32815         var keys = path.split(this.pathSeparator);
32816         var v = keys.pop();
32817         if(keys.length > 0){
32818             var f = function(success, node){
32819                 if(success && node){
32820                     var n = node.findChild(attr, v);
32821                     if(n){
32822                         n.select();
32823                         if(callback){
32824                             callback(true, n);
32825                         }
32826                     }else if(callback){
32827                         callback(false, n);
32828                     }
32829                 }else{
32830                     if(callback){
32831                         callback(false, n);
32832                     }
32833                 }
32834             };
32835             this.expandPath(keys.join(this.pathSeparator), attr, f);
32836         }else{
32837             this.root.select();
32838             if(callback){
32839                 callback(true, this.root);
32840             }
32841         }
32842     },
32843
32844     getTreeEl : function(){
32845         return this.el;
32846     },
32847
32848     /**
32849      * Trigger rendering of this TreePanel
32850      */
32851     render : function(){
32852         if (this.innerCt) {
32853             return this; // stop it rendering more than once!!
32854         }
32855         
32856         this.innerCt = this.el.createChild({tag:"ul",
32857                cls:"x-tree-root-ct " +
32858                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32859
32860         if(this.containerScroll){
32861             Roo.dd.ScrollManager.register(this.el);
32862         }
32863         if((this.enableDD || this.enableDrop) && !this.dropZone){
32864            /**
32865             * The dropZone used by this tree if drop is enabled
32866             * @type Roo.tree.TreeDropZone
32867             */
32868              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32869                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32870            });
32871         }
32872         if((this.enableDD || this.enableDrag) && !this.dragZone){
32873            /**
32874             * The dragZone used by this tree if drag is enabled
32875             * @type Roo.tree.TreeDragZone
32876             */
32877             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32878                ddGroup: this.ddGroup || "TreeDD",
32879                scroll: this.ddScroll
32880            });
32881         }
32882         this.getSelectionModel().init(this);
32883         if (!this.root) {
32884             Roo.log("ROOT not set in tree");
32885             return this;
32886         }
32887         this.root.render();
32888         if(!this.rootVisible){
32889             this.root.renderChildren();
32890         }
32891         return this;
32892     }
32893 });/*
32894  * Based on:
32895  * Ext JS Library 1.1.1
32896  * Copyright(c) 2006-2007, Ext JS, LLC.
32897  *
32898  * Originally Released Under LGPL - original licence link has changed is not relivant.
32899  *
32900  * Fork - LGPL
32901  * <script type="text/javascript">
32902  */
32903  
32904
32905 /**
32906  * @class Roo.tree.DefaultSelectionModel
32907  * @extends Roo.util.Observable
32908  * The default single selection for a TreePanel.
32909  * @param {Object} cfg Configuration
32910  */
32911 Roo.tree.DefaultSelectionModel = function(cfg){
32912    this.selNode = null;
32913    
32914    
32915    
32916    this.addEvents({
32917        /**
32918         * @event selectionchange
32919         * Fires when the selected node changes
32920         * @param {DefaultSelectionModel} this
32921         * @param {TreeNode} node the new selection
32922         */
32923        "selectionchange" : true,
32924
32925        /**
32926         * @event beforeselect
32927         * Fires before the selected node changes, return false to cancel the change
32928         * @param {DefaultSelectionModel} this
32929         * @param {TreeNode} node the new selection
32930         * @param {TreeNode} node the old selection
32931         */
32932        "beforeselect" : true
32933    });
32934    
32935     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32936 };
32937
32938 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32939     init : function(tree){
32940         this.tree = tree;
32941         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32942         tree.on("click", this.onNodeClick, this);
32943     },
32944     
32945     onNodeClick : function(node, e){
32946         if (e.ctrlKey && this.selNode == node)  {
32947             this.unselect(node);
32948             return;
32949         }
32950         this.select(node);
32951     },
32952     
32953     /**
32954      * Select a node.
32955      * @param {TreeNode} node The node to select
32956      * @return {TreeNode} The selected node
32957      */
32958     select : function(node){
32959         var last = this.selNode;
32960         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32961             if(last){
32962                 last.ui.onSelectedChange(false);
32963             }
32964             this.selNode = node;
32965             node.ui.onSelectedChange(true);
32966             this.fireEvent("selectionchange", this, node, last);
32967         }
32968         return node;
32969     },
32970     
32971     /**
32972      * Deselect a node.
32973      * @param {TreeNode} node The node to unselect
32974      */
32975     unselect : function(node){
32976         if(this.selNode == node){
32977             this.clearSelections();
32978         }    
32979     },
32980     
32981     /**
32982      * Clear all selections
32983      */
32984     clearSelections : function(){
32985         var n = this.selNode;
32986         if(n){
32987             n.ui.onSelectedChange(false);
32988             this.selNode = null;
32989             this.fireEvent("selectionchange", this, null);
32990         }
32991         return n;
32992     },
32993     
32994     /**
32995      * Get the selected node
32996      * @return {TreeNode} The selected node
32997      */
32998     getSelectedNode : function(){
32999         return this.selNode;    
33000     },
33001     
33002     /**
33003      * Returns true if the node is selected
33004      * @param {TreeNode} node The node to check
33005      * @return {Boolean}
33006      */
33007     isSelected : function(node){
33008         return this.selNode == node;  
33009     },
33010
33011     /**
33012      * Selects the node above the selected node in the tree, intelligently walking the nodes
33013      * @return TreeNode The new selection
33014      */
33015     selectPrevious : function(){
33016         var s = this.selNode || this.lastSelNode;
33017         if(!s){
33018             return null;
33019         }
33020         var ps = s.previousSibling;
33021         if(ps){
33022             if(!ps.isExpanded() || ps.childNodes.length < 1){
33023                 return this.select(ps);
33024             } else{
33025                 var lc = ps.lastChild;
33026                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
33027                     lc = lc.lastChild;
33028                 }
33029                 return this.select(lc);
33030             }
33031         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33032             return this.select(s.parentNode);
33033         }
33034         return null;
33035     },
33036
33037     /**
33038      * Selects the node above the selected node in the tree, intelligently walking the nodes
33039      * @return TreeNode The new selection
33040      */
33041     selectNext : function(){
33042         var s = this.selNode || this.lastSelNode;
33043         if(!s){
33044             return null;
33045         }
33046         if(s.firstChild && s.isExpanded()){
33047              return this.select(s.firstChild);
33048          }else if(s.nextSibling){
33049              return this.select(s.nextSibling);
33050          }else if(s.parentNode){
33051             var newS = null;
33052             s.parentNode.bubble(function(){
33053                 if(this.nextSibling){
33054                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33055                     return false;
33056                 }
33057             });
33058             return newS;
33059          }
33060         return null;
33061     },
33062
33063     onKeyDown : function(e){
33064         var s = this.selNode || this.lastSelNode;
33065         // undesirable, but required
33066         var sm = this;
33067         if(!s){
33068             return;
33069         }
33070         var k = e.getKey();
33071         switch(k){
33072              case e.DOWN:
33073                  e.stopEvent();
33074                  this.selectNext();
33075              break;
33076              case e.UP:
33077                  e.stopEvent();
33078                  this.selectPrevious();
33079              break;
33080              case e.RIGHT:
33081                  e.preventDefault();
33082                  if(s.hasChildNodes()){
33083                      if(!s.isExpanded()){
33084                          s.expand();
33085                      }else if(s.firstChild){
33086                          this.select(s.firstChild, e);
33087                      }
33088                  }
33089              break;
33090              case e.LEFT:
33091                  e.preventDefault();
33092                  if(s.hasChildNodes() && s.isExpanded()){
33093                      s.collapse();
33094                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33095                      this.select(s.parentNode, e);
33096                  }
33097              break;
33098         };
33099     }
33100 });
33101
33102 /**
33103  * @class Roo.tree.MultiSelectionModel
33104  * @extends Roo.util.Observable
33105  * Multi selection for a TreePanel.
33106  * @param {Object} cfg Configuration
33107  */
33108 Roo.tree.MultiSelectionModel = function(){
33109    this.selNodes = [];
33110    this.selMap = {};
33111    this.addEvents({
33112        /**
33113         * @event selectionchange
33114         * Fires when the selected nodes change
33115         * @param {MultiSelectionModel} this
33116         * @param {Array} nodes Array of the selected nodes
33117         */
33118        "selectionchange" : true
33119    });
33120    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33121    
33122 };
33123
33124 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33125     init : function(tree){
33126         this.tree = tree;
33127         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33128         tree.on("click", this.onNodeClick, this);
33129     },
33130     
33131     onNodeClick : function(node, e){
33132         this.select(node, e, e.ctrlKey);
33133     },
33134     
33135     /**
33136      * Select a node.
33137      * @param {TreeNode} node The node to select
33138      * @param {EventObject} e (optional) An event associated with the selection
33139      * @param {Boolean} keepExisting True to retain existing selections
33140      * @return {TreeNode} The selected node
33141      */
33142     select : function(node, e, keepExisting){
33143         if(keepExisting !== true){
33144             this.clearSelections(true);
33145         }
33146         if(this.isSelected(node)){
33147             this.lastSelNode = node;
33148             return node;
33149         }
33150         this.selNodes.push(node);
33151         this.selMap[node.id] = node;
33152         this.lastSelNode = node;
33153         node.ui.onSelectedChange(true);
33154         this.fireEvent("selectionchange", this, this.selNodes);
33155         return node;
33156     },
33157     
33158     /**
33159      * Deselect a node.
33160      * @param {TreeNode} node The node to unselect
33161      */
33162     unselect : function(node){
33163         if(this.selMap[node.id]){
33164             node.ui.onSelectedChange(false);
33165             var sn = this.selNodes;
33166             var index = -1;
33167             if(sn.indexOf){
33168                 index = sn.indexOf(node);
33169             }else{
33170                 for(var i = 0, len = sn.length; i < len; i++){
33171                     if(sn[i] == node){
33172                         index = i;
33173                         break;
33174                     }
33175                 }
33176             }
33177             if(index != -1){
33178                 this.selNodes.splice(index, 1);
33179             }
33180             delete this.selMap[node.id];
33181             this.fireEvent("selectionchange", this, this.selNodes);
33182         }
33183     },
33184     
33185     /**
33186      * Clear all selections
33187      */
33188     clearSelections : function(suppressEvent){
33189         var sn = this.selNodes;
33190         if(sn.length > 0){
33191             for(var i = 0, len = sn.length; i < len; i++){
33192                 sn[i].ui.onSelectedChange(false);
33193             }
33194             this.selNodes = [];
33195             this.selMap = {};
33196             if(suppressEvent !== true){
33197                 this.fireEvent("selectionchange", this, this.selNodes);
33198             }
33199         }
33200     },
33201     
33202     /**
33203      * Returns true if the node is selected
33204      * @param {TreeNode} node The node to check
33205      * @return {Boolean}
33206      */
33207     isSelected : function(node){
33208         return this.selMap[node.id] ? true : false;  
33209     },
33210     
33211     /**
33212      * Returns an array of the selected nodes
33213      * @return {Array}
33214      */
33215     getSelectedNodes : function(){
33216         return this.selNodes;    
33217     },
33218
33219     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33220
33221     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33222
33223     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33224 });/*
33225  * Based on:
33226  * Ext JS Library 1.1.1
33227  * Copyright(c) 2006-2007, Ext JS, LLC.
33228  *
33229  * Originally Released Under LGPL - original licence link has changed is not relivant.
33230  *
33231  * Fork - LGPL
33232  * <script type="text/javascript">
33233  */
33234  
33235 /**
33236  * @class Roo.tree.TreeNode
33237  * @extends Roo.data.Node
33238  * @cfg {String} text The text for this node
33239  * @cfg {Boolean} expanded true to start the node expanded
33240  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33241  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33242  * @cfg {Boolean} disabled true to start the node disabled
33243  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33244  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33245  * @cfg {String} cls A css class to be added to the node
33246  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33247  * @cfg {String} href URL of the link used for the node (defaults to #)
33248  * @cfg {String} hrefTarget target frame for the link
33249  * @cfg {String} qtip An Ext QuickTip for the node
33250  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33251  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33252  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33253  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33254  * (defaults to undefined with no checkbox rendered)
33255  * @constructor
33256  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33257  */
33258 Roo.tree.TreeNode = function(attributes){
33259     attributes = attributes || {};
33260     if(typeof attributes == "string"){
33261         attributes = {text: attributes};
33262     }
33263     this.childrenRendered = false;
33264     this.rendered = false;
33265     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33266     this.expanded = attributes.expanded === true;
33267     this.isTarget = attributes.isTarget !== false;
33268     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33269     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33270
33271     /**
33272      * Read-only. The text for this node. To change it use setText().
33273      * @type String
33274      */
33275     this.text = attributes.text;
33276     /**
33277      * True if this node is disabled.
33278      * @type Boolean
33279      */
33280     this.disabled = attributes.disabled === true;
33281
33282     this.addEvents({
33283         /**
33284         * @event textchange
33285         * Fires when the text for this node is changed
33286         * @param {Node} this This node
33287         * @param {String} text The new text
33288         * @param {String} oldText The old text
33289         */
33290         "textchange" : true,
33291         /**
33292         * @event beforeexpand
33293         * Fires before this node is expanded, return false to cancel.
33294         * @param {Node} this This node
33295         * @param {Boolean} deep
33296         * @param {Boolean} anim
33297         */
33298         "beforeexpand" : true,
33299         /**
33300         * @event beforecollapse
33301         * Fires before this node is collapsed, return false to cancel.
33302         * @param {Node} this This node
33303         * @param {Boolean} deep
33304         * @param {Boolean} anim
33305         */
33306         "beforecollapse" : true,
33307         /**
33308         * @event expand
33309         * Fires when this node is expanded
33310         * @param {Node} this This node
33311         */
33312         "expand" : true,
33313         /**
33314         * @event disabledchange
33315         * Fires when the disabled status of this node changes
33316         * @param {Node} this This node
33317         * @param {Boolean} disabled
33318         */
33319         "disabledchange" : true,
33320         /**
33321         * @event collapse
33322         * Fires when this node is collapsed
33323         * @param {Node} this This node
33324         */
33325         "collapse" : true,
33326         /**
33327         * @event beforeclick
33328         * Fires before click processing. Return false to cancel the default action.
33329         * @param {Node} this This node
33330         * @param {Roo.EventObject} e The event object
33331         */
33332         "beforeclick":true,
33333         /**
33334         * @event checkchange
33335         * Fires when a node with a checkbox's checked property changes
33336         * @param {Node} this This node
33337         * @param {Boolean} checked
33338         */
33339         "checkchange":true,
33340         /**
33341         * @event click
33342         * Fires when this node is clicked
33343         * @param {Node} this This node
33344         * @param {Roo.EventObject} e The event object
33345         */
33346         "click":true,
33347         /**
33348         * @event dblclick
33349         * Fires when this node is double clicked
33350         * @param {Node} this This node
33351         * @param {Roo.EventObject} e The event object
33352         */
33353         "dblclick":true,
33354         /**
33355         * @event contextmenu
33356         * Fires when this node is right clicked
33357         * @param {Node} this This node
33358         * @param {Roo.EventObject} e The event object
33359         */
33360         "contextmenu":true,
33361         /**
33362         * @event beforechildrenrendered
33363         * Fires right before the child nodes for this node are rendered
33364         * @param {Node} this This node
33365         */
33366         "beforechildrenrendered":true
33367     });
33368
33369     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33370
33371     /**
33372      * Read-only. The UI for this node
33373      * @type TreeNodeUI
33374      */
33375     this.ui = new uiClass(this);
33376     
33377     // finally support items[]
33378     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33379         return;
33380     }
33381     
33382     
33383     Roo.each(this.attributes.items, function(c) {
33384         this.appendChild(Roo.factory(c,Roo.Tree));
33385     }, this);
33386     delete this.attributes.items;
33387     
33388     
33389     
33390 };
33391 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33392     preventHScroll: true,
33393     /**
33394      * Returns true if this node is expanded
33395      * @return {Boolean}
33396      */
33397     isExpanded : function(){
33398         return this.expanded;
33399     },
33400
33401     /**
33402      * Returns the UI object for this node
33403      * @return {TreeNodeUI}
33404      */
33405     getUI : function(){
33406         return this.ui;
33407     },
33408
33409     // private override
33410     setFirstChild : function(node){
33411         var of = this.firstChild;
33412         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33413         if(this.childrenRendered && of && node != of){
33414             of.renderIndent(true, true);
33415         }
33416         if(this.rendered){
33417             this.renderIndent(true, true);
33418         }
33419     },
33420
33421     // private override
33422     setLastChild : function(node){
33423         var ol = this.lastChild;
33424         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33425         if(this.childrenRendered && ol && node != ol){
33426             ol.renderIndent(true, true);
33427         }
33428         if(this.rendered){
33429             this.renderIndent(true, true);
33430         }
33431     },
33432
33433     // these methods are overridden to provide lazy rendering support
33434     // private override
33435     appendChild : function()
33436     {
33437         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33438         if(node && this.childrenRendered){
33439             node.render();
33440         }
33441         this.ui.updateExpandIcon();
33442         return node;
33443     },
33444
33445     // private override
33446     removeChild : function(node){
33447         this.ownerTree.getSelectionModel().unselect(node);
33448         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33449         // if it's been rendered remove dom node
33450         if(this.childrenRendered){
33451             node.ui.remove();
33452         }
33453         if(this.childNodes.length < 1){
33454             this.collapse(false, false);
33455         }else{
33456             this.ui.updateExpandIcon();
33457         }
33458         if(!this.firstChild) {
33459             this.childrenRendered = false;
33460         }
33461         return node;
33462     },
33463
33464     // private override
33465     insertBefore : function(node, refNode){
33466         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33467         if(newNode && refNode && this.childrenRendered){
33468             node.render();
33469         }
33470         this.ui.updateExpandIcon();
33471         return newNode;
33472     },
33473
33474     /**
33475      * Sets the text for this node
33476      * @param {String} text
33477      */
33478     setText : function(text){
33479         var oldText = this.text;
33480         this.text = text;
33481         this.attributes.text = text;
33482         if(this.rendered){ // event without subscribing
33483             this.ui.onTextChange(this, text, oldText);
33484         }
33485         this.fireEvent("textchange", this, text, oldText);
33486     },
33487
33488     /**
33489      * Triggers selection of this node
33490      */
33491     select : function(){
33492         this.getOwnerTree().getSelectionModel().select(this);
33493     },
33494
33495     /**
33496      * Triggers deselection of this node
33497      */
33498     unselect : function(){
33499         this.getOwnerTree().getSelectionModel().unselect(this);
33500     },
33501
33502     /**
33503      * Returns true if this node is selected
33504      * @return {Boolean}
33505      */
33506     isSelected : function(){
33507         return this.getOwnerTree().getSelectionModel().isSelected(this);
33508     },
33509
33510     /**
33511      * Expand this node.
33512      * @param {Boolean} deep (optional) True to expand all children as well
33513      * @param {Boolean} anim (optional) false to cancel the default animation
33514      * @param {Function} callback (optional) A callback to be called when
33515      * expanding this node completes (does not wait for deep expand to complete).
33516      * Called with 1 parameter, this node.
33517      */
33518     expand : function(deep, anim, callback){
33519         if(!this.expanded){
33520             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33521                 return;
33522             }
33523             if(!this.childrenRendered){
33524                 this.renderChildren();
33525             }
33526             this.expanded = true;
33527             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33528                 this.ui.animExpand(function(){
33529                     this.fireEvent("expand", this);
33530                     if(typeof callback == "function"){
33531                         callback(this);
33532                     }
33533                     if(deep === true){
33534                         this.expandChildNodes(true);
33535                     }
33536                 }.createDelegate(this));
33537                 return;
33538             }else{
33539                 this.ui.expand();
33540                 this.fireEvent("expand", this);
33541                 if(typeof callback == "function"){
33542                     callback(this);
33543                 }
33544             }
33545         }else{
33546            if(typeof callback == "function"){
33547                callback(this);
33548            }
33549         }
33550         if(deep === true){
33551             this.expandChildNodes(true);
33552         }
33553     },
33554
33555     isHiddenRoot : function(){
33556         return this.isRoot && !this.getOwnerTree().rootVisible;
33557     },
33558
33559     /**
33560      * Collapse this node.
33561      * @param {Boolean} deep (optional) True to collapse all children as well
33562      * @param {Boolean} anim (optional) false to cancel the default animation
33563      */
33564     collapse : function(deep, anim){
33565         if(this.expanded && !this.isHiddenRoot()){
33566             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33567                 return;
33568             }
33569             this.expanded = false;
33570             if((this.getOwnerTree().animate && anim !== false) || anim){
33571                 this.ui.animCollapse(function(){
33572                     this.fireEvent("collapse", this);
33573                     if(deep === true){
33574                         this.collapseChildNodes(true);
33575                     }
33576                 }.createDelegate(this));
33577                 return;
33578             }else{
33579                 this.ui.collapse();
33580                 this.fireEvent("collapse", this);
33581             }
33582         }
33583         if(deep === true){
33584             var cs = this.childNodes;
33585             for(var i = 0, len = cs.length; i < len; i++) {
33586                 cs[i].collapse(true, false);
33587             }
33588         }
33589     },
33590
33591     // private
33592     delayedExpand : function(delay){
33593         if(!this.expandProcId){
33594             this.expandProcId = this.expand.defer(delay, this);
33595         }
33596     },
33597
33598     // private
33599     cancelExpand : function(){
33600         if(this.expandProcId){
33601             clearTimeout(this.expandProcId);
33602         }
33603         this.expandProcId = false;
33604     },
33605
33606     /**
33607      * Toggles expanded/collapsed state of the node
33608      */
33609     toggle : function(){
33610         if(this.expanded){
33611             this.collapse();
33612         }else{
33613             this.expand();
33614         }
33615     },
33616
33617     /**
33618      * Ensures all parent nodes are expanded
33619      */
33620     ensureVisible : function(callback){
33621         var tree = this.getOwnerTree();
33622         tree.expandPath(this.parentNode.getPath(), false, function(){
33623             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33624             Roo.callback(callback);
33625         }.createDelegate(this));
33626     },
33627
33628     /**
33629      * Expand all child nodes
33630      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33631      */
33632     expandChildNodes : function(deep){
33633         var cs = this.childNodes;
33634         for(var i = 0, len = cs.length; i < len; i++) {
33635                 cs[i].expand(deep);
33636         }
33637     },
33638
33639     /**
33640      * Collapse all child nodes
33641      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33642      */
33643     collapseChildNodes : function(deep){
33644         var cs = this.childNodes;
33645         for(var i = 0, len = cs.length; i < len; i++) {
33646                 cs[i].collapse(deep);
33647         }
33648     },
33649
33650     /**
33651      * Disables this node
33652      */
33653     disable : function(){
33654         this.disabled = true;
33655         this.unselect();
33656         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33657             this.ui.onDisableChange(this, true);
33658         }
33659         this.fireEvent("disabledchange", this, true);
33660     },
33661
33662     /**
33663      * Enables this node
33664      */
33665     enable : function(){
33666         this.disabled = false;
33667         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33668             this.ui.onDisableChange(this, false);
33669         }
33670         this.fireEvent("disabledchange", this, false);
33671     },
33672
33673     // private
33674     renderChildren : function(suppressEvent){
33675         if(suppressEvent !== false){
33676             this.fireEvent("beforechildrenrendered", this);
33677         }
33678         var cs = this.childNodes;
33679         for(var i = 0, len = cs.length; i < len; i++){
33680             cs[i].render(true);
33681         }
33682         this.childrenRendered = true;
33683     },
33684
33685     // private
33686     sort : function(fn, scope){
33687         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33688         if(this.childrenRendered){
33689             var cs = this.childNodes;
33690             for(var i = 0, len = cs.length; i < len; i++){
33691                 cs[i].render(true);
33692             }
33693         }
33694     },
33695
33696     // private
33697     render : function(bulkRender){
33698         this.ui.render(bulkRender);
33699         if(!this.rendered){
33700             this.rendered = true;
33701             if(this.expanded){
33702                 this.expanded = false;
33703                 this.expand(false, false);
33704             }
33705         }
33706     },
33707
33708     // private
33709     renderIndent : function(deep, refresh){
33710         if(refresh){
33711             this.ui.childIndent = null;
33712         }
33713         this.ui.renderIndent();
33714         if(deep === true && this.childrenRendered){
33715             var cs = this.childNodes;
33716             for(var i = 0, len = cs.length; i < len; i++){
33717                 cs[i].renderIndent(true, refresh);
33718             }
33719         }
33720     }
33721 });/*
33722  * Based on:
33723  * Ext JS Library 1.1.1
33724  * Copyright(c) 2006-2007, Ext JS, LLC.
33725  *
33726  * Originally Released Under LGPL - original licence link has changed is not relivant.
33727  *
33728  * Fork - LGPL
33729  * <script type="text/javascript">
33730  */
33731  
33732 /**
33733  * @class Roo.tree.AsyncTreeNode
33734  * @extends Roo.tree.TreeNode
33735  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33736  * @constructor
33737  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33738  */
33739  Roo.tree.AsyncTreeNode = function(config){
33740     this.loaded = false;
33741     this.loading = false;
33742     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33743     /**
33744     * @event beforeload
33745     * Fires before this node is loaded, return false to cancel
33746     * @param {Node} this This node
33747     */
33748     this.addEvents({'beforeload':true, 'load': true});
33749     /**
33750     * @event load
33751     * Fires when this node is loaded
33752     * @param {Node} this This node
33753     */
33754     /**
33755      * The loader used by this node (defaults to using the tree's defined loader)
33756      * @type TreeLoader
33757      * @property loader
33758      */
33759 };
33760 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33761     expand : function(deep, anim, callback){
33762         if(this.loading){ // if an async load is already running, waiting til it's done
33763             var timer;
33764             var f = function(){
33765                 if(!this.loading){ // done loading
33766                     clearInterval(timer);
33767                     this.expand(deep, anim, callback);
33768                 }
33769             }.createDelegate(this);
33770             timer = setInterval(f, 200);
33771             return;
33772         }
33773         if(!this.loaded){
33774             if(this.fireEvent("beforeload", this) === false){
33775                 return;
33776             }
33777             this.loading = true;
33778             this.ui.beforeLoad(this);
33779             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33780             if(loader){
33781                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33782                 return;
33783             }
33784         }
33785         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33786     },
33787     
33788     /**
33789      * Returns true if this node is currently loading
33790      * @return {Boolean}
33791      */
33792     isLoading : function(){
33793         return this.loading;  
33794     },
33795     
33796     loadComplete : function(deep, anim, callback){
33797         this.loading = false;
33798         this.loaded = true;
33799         this.ui.afterLoad(this);
33800         this.fireEvent("load", this);
33801         this.expand(deep, anim, callback);
33802     },
33803     
33804     /**
33805      * Returns true if this node has been loaded
33806      * @return {Boolean}
33807      */
33808     isLoaded : function(){
33809         return this.loaded;
33810     },
33811     
33812     hasChildNodes : function(){
33813         if(!this.isLeaf() && !this.loaded){
33814             return true;
33815         }else{
33816             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33817         }
33818     },
33819
33820     /**
33821      * Trigger a reload for this node
33822      * @param {Function} callback
33823      */
33824     reload : function(callback){
33825         this.collapse(false, false);
33826         while(this.firstChild){
33827             this.removeChild(this.firstChild);
33828         }
33829         this.childrenRendered = false;
33830         this.loaded = false;
33831         if(this.isHiddenRoot()){
33832             this.expanded = false;
33833         }
33834         this.expand(false, false, callback);
33835     }
33836 });/*
33837  * Based on:
33838  * Ext JS Library 1.1.1
33839  * Copyright(c) 2006-2007, Ext JS, LLC.
33840  *
33841  * Originally Released Under LGPL - original licence link has changed is not relivant.
33842  *
33843  * Fork - LGPL
33844  * <script type="text/javascript">
33845  */
33846  
33847 /**
33848  * @class Roo.tree.TreeNodeUI
33849  * @constructor
33850  * @param {Object} node The node to render
33851  * The TreeNode UI implementation is separate from the
33852  * tree implementation. Unless you are customizing the tree UI,
33853  * you should never have to use this directly.
33854  */
33855 Roo.tree.TreeNodeUI = function(node){
33856     this.node = node;
33857     this.rendered = false;
33858     this.animating = false;
33859     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33860 };
33861
33862 Roo.tree.TreeNodeUI.prototype = {
33863     removeChild : function(node){
33864         if(this.rendered){
33865             this.ctNode.removeChild(node.ui.getEl());
33866         }
33867     },
33868
33869     beforeLoad : function(){
33870          this.addClass("x-tree-node-loading");
33871     },
33872
33873     afterLoad : function(){
33874          this.removeClass("x-tree-node-loading");
33875     },
33876
33877     onTextChange : function(node, text, oldText){
33878         if(this.rendered){
33879             this.textNode.innerHTML = text;
33880         }
33881     },
33882
33883     onDisableChange : function(node, state){
33884         this.disabled = state;
33885         if(state){
33886             this.addClass("x-tree-node-disabled");
33887         }else{
33888             this.removeClass("x-tree-node-disabled");
33889         }
33890     },
33891
33892     onSelectedChange : function(state){
33893         if(state){
33894             this.focus();
33895             this.addClass("x-tree-selected");
33896         }else{
33897             //this.blur();
33898             this.removeClass("x-tree-selected");
33899         }
33900     },
33901
33902     onMove : function(tree, node, oldParent, newParent, index, refNode){
33903         this.childIndent = null;
33904         if(this.rendered){
33905             var targetNode = newParent.ui.getContainer();
33906             if(!targetNode){//target not rendered
33907                 this.holder = document.createElement("div");
33908                 this.holder.appendChild(this.wrap);
33909                 return;
33910             }
33911             var insertBefore = refNode ? refNode.ui.getEl() : null;
33912             if(insertBefore){
33913                 targetNode.insertBefore(this.wrap, insertBefore);
33914             }else{
33915                 targetNode.appendChild(this.wrap);
33916             }
33917             this.node.renderIndent(true);
33918         }
33919     },
33920
33921     addClass : function(cls){
33922         if(this.elNode){
33923             Roo.fly(this.elNode).addClass(cls);
33924         }
33925     },
33926
33927     removeClass : function(cls){
33928         if(this.elNode){
33929             Roo.fly(this.elNode).removeClass(cls);
33930         }
33931     },
33932
33933     remove : function(){
33934         if(this.rendered){
33935             this.holder = document.createElement("div");
33936             this.holder.appendChild(this.wrap);
33937         }
33938     },
33939
33940     fireEvent : function(){
33941         return this.node.fireEvent.apply(this.node, arguments);
33942     },
33943
33944     initEvents : function(){
33945         this.node.on("move", this.onMove, this);
33946         var E = Roo.EventManager;
33947         var a = this.anchor;
33948
33949         var el = Roo.fly(a, '_treeui');
33950
33951         if(Roo.isOpera){ // opera render bug ignores the CSS
33952             el.setStyle("text-decoration", "none");
33953         }
33954
33955         el.on("click", this.onClick, this);
33956         el.on("dblclick", this.onDblClick, this);
33957
33958         if(this.checkbox){
33959             Roo.EventManager.on(this.checkbox,
33960                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33961         }
33962
33963         el.on("contextmenu", this.onContextMenu, this);
33964
33965         var icon = Roo.fly(this.iconNode);
33966         icon.on("click", this.onClick, this);
33967         icon.on("dblclick", this.onDblClick, this);
33968         icon.on("contextmenu", this.onContextMenu, this);
33969         E.on(this.ecNode, "click", this.ecClick, this, true);
33970
33971         if(this.node.disabled){
33972             this.addClass("x-tree-node-disabled");
33973         }
33974         if(this.node.hidden){
33975             this.addClass("x-tree-node-disabled");
33976         }
33977         var ot = this.node.getOwnerTree();
33978         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33979         if(dd && (!this.node.isRoot || ot.rootVisible)){
33980             Roo.dd.Registry.register(this.elNode, {
33981                 node: this.node,
33982                 handles: this.getDDHandles(),
33983                 isHandle: false
33984             });
33985         }
33986     },
33987
33988     getDDHandles : function(){
33989         return [this.iconNode, this.textNode];
33990     },
33991
33992     hide : function(){
33993         if(this.rendered){
33994             this.wrap.style.display = "none";
33995         }
33996     },
33997
33998     show : function(){
33999         if(this.rendered){
34000             this.wrap.style.display = "";
34001         }
34002     },
34003
34004     onContextMenu : function(e){
34005         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
34006             e.preventDefault();
34007             this.focus();
34008             this.fireEvent("contextmenu", this.node, e);
34009         }
34010     },
34011
34012     onClick : function(e){
34013         if(this.dropping){
34014             e.stopEvent();
34015             return;
34016         }
34017         if(this.fireEvent("beforeclick", this.node, e) !== false){
34018             if(!this.disabled && this.node.attributes.href){
34019                 this.fireEvent("click", this.node, e);
34020                 return;
34021             }
34022             e.preventDefault();
34023             if(this.disabled){
34024                 return;
34025             }
34026
34027             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34028                 this.node.toggle();
34029             }
34030
34031             this.fireEvent("click", this.node, e);
34032         }else{
34033             e.stopEvent();
34034         }
34035     },
34036
34037     onDblClick : function(e){
34038         e.preventDefault();
34039         if(this.disabled){
34040             return;
34041         }
34042         if(this.checkbox){
34043             this.toggleCheck();
34044         }
34045         if(!this.animating && this.node.hasChildNodes()){
34046             this.node.toggle();
34047         }
34048         this.fireEvent("dblclick", this.node, e);
34049     },
34050
34051     onCheckChange : function(){
34052         var checked = this.checkbox.checked;
34053         this.node.attributes.checked = checked;
34054         this.fireEvent('checkchange', this.node, checked);
34055     },
34056
34057     ecClick : function(e){
34058         if(!this.animating && this.node.hasChildNodes()){
34059             this.node.toggle();
34060         }
34061     },
34062
34063     startDrop : function(){
34064         this.dropping = true;
34065     },
34066
34067     // delayed drop so the click event doesn't get fired on a drop
34068     endDrop : function(){
34069        setTimeout(function(){
34070            this.dropping = false;
34071        }.createDelegate(this), 50);
34072     },
34073
34074     expand : function(){
34075         this.updateExpandIcon();
34076         this.ctNode.style.display = "";
34077     },
34078
34079     focus : function(){
34080         if(!this.node.preventHScroll){
34081             try{this.anchor.focus();
34082             }catch(e){}
34083         }else if(!Roo.isIE){
34084             try{
34085                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34086                 var l = noscroll.scrollLeft;
34087                 this.anchor.focus();
34088                 noscroll.scrollLeft = l;
34089             }catch(e){}
34090         }
34091     },
34092
34093     toggleCheck : function(value){
34094         var cb = this.checkbox;
34095         if(cb){
34096             cb.checked = (value === undefined ? !cb.checked : value);
34097         }
34098     },
34099
34100     blur : function(){
34101         try{
34102             this.anchor.blur();
34103         }catch(e){}
34104     },
34105
34106     animExpand : function(callback){
34107         var ct = Roo.get(this.ctNode);
34108         ct.stopFx();
34109         if(!this.node.hasChildNodes()){
34110             this.updateExpandIcon();
34111             this.ctNode.style.display = "";
34112             Roo.callback(callback);
34113             return;
34114         }
34115         this.animating = true;
34116         this.updateExpandIcon();
34117
34118         ct.slideIn('t', {
34119            callback : function(){
34120                this.animating = false;
34121                Roo.callback(callback);
34122             },
34123             scope: this,
34124             duration: this.node.ownerTree.duration || .25
34125         });
34126     },
34127
34128     highlight : function(){
34129         var tree = this.node.getOwnerTree();
34130         Roo.fly(this.wrap).highlight(
34131             tree.hlColor || "C3DAF9",
34132             {endColor: tree.hlBaseColor}
34133         );
34134     },
34135
34136     collapse : function(){
34137         this.updateExpandIcon();
34138         this.ctNode.style.display = "none";
34139     },
34140
34141     animCollapse : function(callback){
34142         var ct = Roo.get(this.ctNode);
34143         ct.enableDisplayMode('block');
34144         ct.stopFx();
34145
34146         this.animating = true;
34147         this.updateExpandIcon();
34148
34149         ct.slideOut('t', {
34150             callback : function(){
34151                this.animating = false;
34152                Roo.callback(callback);
34153             },
34154             scope: this,
34155             duration: this.node.ownerTree.duration || .25
34156         });
34157     },
34158
34159     getContainer : function(){
34160         return this.ctNode;
34161     },
34162
34163     getEl : function(){
34164         return this.wrap;
34165     },
34166
34167     appendDDGhost : function(ghostNode){
34168         ghostNode.appendChild(this.elNode.cloneNode(true));
34169     },
34170
34171     getDDRepairXY : function(){
34172         return Roo.lib.Dom.getXY(this.iconNode);
34173     },
34174
34175     onRender : function(){
34176         this.render();
34177     },
34178
34179     render : function(bulkRender){
34180         var n = this.node, a = n.attributes;
34181         var targetNode = n.parentNode ?
34182               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34183
34184         if(!this.rendered){
34185             this.rendered = true;
34186
34187             this.renderElements(n, a, targetNode, bulkRender);
34188
34189             if(a.qtip){
34190                if(this.textNode.setAttributeNS){
34191                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34192                    if(a.qtipTitle){
34193                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34194                    }
34195                }else{
34196                    this.textNode.setAttribute("ext:qtip", a.qtip);
34197                    if(a.qtipTitle){
34198                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34199                    }
34200                }
34201             }else if(a.qtipCfg){
34202                 a.qtipCfg.target = Roo.id(this.textNode);
34203                 Roo.QuickTips.register(a.qtipCfg);
34204             }
34205             this.initEvents();
34206             if(!this.node.expanded){
34207                 this.updateExpandIcon();
34208             }
34209         }else{
34210             if(bulkRender === true) {
34211                 targetNode.appendChild(this.wrap);
34212             }
34213         }
34214     },
34215
34216     renderElements : function(n, a, targetNode, bulkRender)
34217     {
34218         // add some indent caching, this helps performance when rendering a large tree
34219         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34220         var t = n.getOwnerTree();
34221         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34222         if (typeof(n.attributes.html) != 'undefined') {
34223             txt = n.attributes.html;
34224         }
34225         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34226         var cb = typeof a.checked == 'boolean';
34227         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34228         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34229             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34230             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34231             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34232             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34233             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34234              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34235                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34236             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34237             "</li>"];
34238
34239         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34240             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34241                                 n.nextSibling.ui.getEl(), buf.join(""));
34242         }else{
34243             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34244         }
34245
34246         this.elNode = this.wrap.childNodes[0];
34247         this.ctNode = this.wrap.childNodes[1];
34248         var cs = this.elNode.childNodes;
34249         this.indentNode = cs[0];
34250         this.ecNode = cs[1];
34251         this.iconNode = cs[2];
34252         var index = 3;
34253         if(cb){
34254             this.checkbox = cs[3];
34255             index++;
34256         }
34257         this.anchor = cs[index];
34258         this.textNode = cs[index].firstChild;
34259     },
34260
34261     getAnchor : function(){
34262         return this.anchor;
34263     },
34264
34265     getTextEl : function(){
34266         return this.textNode;
34267     },
34268
34269     getIconEl : function(){
34270         return this.iconNode;
34271     },
34272
34273     isChecked : function(){
34274         return this.checkbox ? this.checkbox.checked : false;
34275     },
34276
34277     updateExpandIcon : function(){
34278         if(this.rendered){
34279             var n = this.node, c1, c2;
34280             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34281             var hasChild = n.hasChildNodes();
34282             if(hasChild){
34283                 if(n.expanded){
34284                     cls += "-minus";
34285                     c1 = "x-tree-node-collapsed";
34286                     c2 = "x-tree-node-expanded";
34287                 }else{
34288                     cls += "-plus";
34289                     c1 = "x-tree-node-expanded";
34290                     c2 = "x-tree-node-collapsed";
34291                 }
34292                 if(this.wasLeaf){
34293                     this.removeClass("x-tree-node-leaf");
34294                     this.wasLeaf = false;
34295                 }
34296                 if(this.c1 != c1 || this.c2 != c2){
34297                     Roo.fly(this.elNode).replaceClass(c1, c2);
34298                     this.c1 = c1; this.c2 = c2;
34299                 }
34300             }else{
34301                 // this changes non-leafs into leafs if they have no children.
34302                 // it's not very rational behaviour..
34303                 
34304                 if(!this.wasLeaf && this.node.leaf){
34305                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34306                     delete this.c1;
34307                     delete this.c2;
34308                     this.wasLeaf = true;
34309                 }
34310             }
34311             var ecc = "x-tree-ec-icon "+cls;
34312             if(this.ecc != ecc){
34313                 this.ecNode.className = ecc;
34314                 this.ecc = ecc;
34315             }
34316         }
34317     },
34318
34319     getChildIndent : function(){
34320         if(!this.childIndent){
34321             var buf = [];
34322             var p = this.node;
34323             while(p){
34324                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34325                     if(!p.isLast()) {
34326                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34327                     } else {
34328                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34329                     }
34330                 }
34331                 p = p.parentNode;
34332             }
34333             this.childIndent = buf.join("");
34334         }
34335         return this.childIndent;
34336     },
34337
34338     renderIndent : function(){
34339         if(this.rendered){
34340             var indent = "";
34341             var p = this.node.parentNode;
34342             if(p){
34343                 indent = p.ui.getChildIndent();
34344             }
34345             if(this.indentMarkup != indent){ // don't rerender if not required
34346                 this.indentNode.innerHTML = indent;
34347                 this.indentMarkup = indent;
34348             }
34349             this.updateExpandIcon();
34350         }
34351     }
34352 };
34353
34354 Roo.tree.RootTreeNodeUI = function(){
34355     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34356 };
34357 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34358     render : function(){
34359         if(!this.rendered){
34360             var targetNode = this.node.ownerTree.innerCt.dom;
34361             this.node.expanded = true;
34362             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34363             this.wrap = this.ctNode = targetNode.firstChild;
34364         }
34365     },
34366     collapse : function(){
34367     },
34368     expand : function(){
34369     }
34370 });/*
34371  * Based on:
34372  * Ext JS Library 1.1.1
34373  * Copyright(c) 2006-2007, Ext JS, LLC.
34374  *
34375  * Originally Released Under LGPL - original licence link has changed is not relivant.
34376  *
34377  * Fork - LGPL
34378  * <script type="text/javascript">
34379  */
34380 /**
34381  * @class Roo.tree.TreeLoader
34382  * @extends Roo.util.Observable
34383  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34384  * nodes from a specified URL. The response must be a javascript Array definition
34385  * who's elements are node definition objects. eg:
34386  * <pre><code>
34387 {  success : true,
34388    data :      [
34389    
34390     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34391     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34392     ]
34393 }
34394
34395
34396 </code></pre>
34397  * <br><br>
34398  * The old style respose with just an array is still supported, but not recommended.
34399  * <br><br>
34400  *
34401  * A server request is sent, and child nodes are loaded only when a node is expanded.
34402  * The loading node's id is passed to the server under the parameter name "node" to
34403  * enable the server to produce the correct child nodes.
34404  * <br><br>
34405  * To pass extra parameters, an event handler may be attached to the "beforeload"
34406  * event, and the parameters specified in the TreeLoader's baseParams property:
34407  * <pre><code>
34408     myTreeLoader.on("beforeload", function(treeLoader, node) {
34409         this.baseParams.category = node.attributes.category;
34410     }, this);
34411 </code></pre><
34412  * This would pass an HTTP parameter called "category" to the server containing
34413  * the value of the Node's "category" attribute.
34414  * @constructor
34415  * Creates a new Treeloader.
34416  * @param {Object} config A config object containing config properties.
34417  */
34418 Roo.tree.TreeLoader = function(config){
34419     this.baseParams = {};
34420     this.requestMethod = "POST";
34421     Roo.apply(this, config);
34422
34423     this.addEvents({
34424     
34425         /**
34426          * @event beforeload
34427          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34428          * @param {Object} This TreeLoader object.
34429          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34430          * @param {Object} callback The callback function specified in the {@link #load} call.
34431          */
34432         beforeload : true,
34433         /**
34434          * @event load
34435          * Fires when the node has been successfuly loaded.
34436          * @param {Object} This TreeLoader object.
34437          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34438          * @param {Object} response The response object containing the data from the server.
34439          */
34440         load : true,
34441         /**
34442          * @event loadexception
34443          * Fires if the network request failed.
34444          * @param {Object} This TreeLoader object.
34445          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34446          * @param {Object} response The response object containing the data from the server.
34447          */
34448         loadexception : true,
34449         /**
34450          * @event create
34451          * Fires before a node is created, enabling you to return custom Node types 
34452          * @param {Object} This TreeLoader object.
34453          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34454          */
34455         create : true
34456     });
34457
34458     Roo.tree.TreeLoader.superclass.constructor.call(this);
34459 };
34460
34461 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34462     /**
34463     * @cfg {String} dataUrl The URL from which to request a Json string which
34464     * specifies an array of node definition object representing the child nodes
34465     * to be loaded.
34466     */
34467     /**
34468     * @cfg {String} requestMethod either GET or POST
34469     * defaults to POST (due to BC)
34470     * to be loaded.
34471     */
34472     /**
34473     * @cfg {Object} baseParams (optional) An object containing properties which
34474     * specify HTTP parameters to be passed to each request for child nodes.
34475     */
34476     /**
34477     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34478     * created by this loader. If the attributes sent by the server have an attribute in this object,
34479     * they take priority.
34480     */
34481     /**
34482     * @cfg {Object} uiProviders (optional) An object containing properties which
34483     * 
34484     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34485     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34486     * <i>uiProvider</i> attribute of a returned child node is a string rather
34487     * than a reference to a TreeNodeUI implementation, this that string value
34488     * is used as a property name in the uiProviders object. You can define the provider named
34489     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34490     */
34491     uiProviders : {},
34492
34493     /**
34494     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34495     * child nodes before loading.
34496     */
34497     clearOnLoad : true,
34498
34499     /**
34500     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34501     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34502     * Grid query { data : [ .....] }
34503     */
34504     
34505     root : false,
34506      /**
34507     * @cfg {String} queryParam (optional) 
34508     * Name of the query as it will be passed on the querystring (defaults to 'node')
34509     * eg. the request will be ?node=[id]
34510     */
34511     
34512     
34513     queryParam: false,
34514     
34515     /**
34516      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34517      * This is called automatically when a node is expanded, but may be used to reload
34518      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34519      * @param {Roo.tree.TreeNode} node
34520      * @param {Function} callback
34521      */
34522     load : function(node, callback){
34523         if(this.clearOnLoad){
34524             while(node.firstChild){
34525                 node.removeChild(node.firstChild);
34526             }
34527         }
34528         if(node.attributes.children){ // preloaded json children
34529             var cs = node.attributes.children;
34530             for(var i = 0, len = cs.length; i < len; i++){
34531                 node.appendChild(this.createNode(cs[i]));
34532             }
34533             if(typeof callback == "function"){
34534                 callback();
34535             }
34536         }else if(this.dataUrl){
34537             this.requestData(node, callback);
34538         }
34539     },
34540
34541     getParams: function(node){
34542         var buf = [], bp = this.baseParams;
34543         for(var key in bp){
34544             if(typeof bp[key] != "function"){
34545                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34546             }
34547         }
34548         var n = this.queryParam === false ? 'node' : this.queryParam;
34549         buf.push(n + "=", encodeURIComponent(node.id));
34550         return buf.join("");
34551     },
34552
34553     requestData : function(node, callback){
34554         if(this.fireEvent("beforeload", this, node, callback) !== false){
34555             this.transId = Roo.Ajax.request({
34556                 method:this.requestMethod,
34557                 url: this.dataUrl||this.url,
34558                 success: this.handleResponse,
34559                 failure: this.handleFailure,
34560                 scope: this,
34561                 argument: {callback: callback, node: node},
34562                 params: this.getParams(node)
34563             });
34564         }else{
34565             // if the load is cancelled, make sure we notify
34566             // the node that we are done
34567             if(typeof callback == "function"){
34568                 callback();
34569             }
34570         }
34571     },
34572
34573     isLoading : function(){
34574         return this.transId ? true : false;
34575     },
34576
34577     abort : function(){
34578         if(this.isLoading()){
34579             Roo.Ajax.abort(this.transId);
34580         }
34581     },
34582
34583     // private
34584     createNode : function(attr)
34585     {
34586         // apply baseAttrs, nice idea Corey!
34587         if(this.baseAttrs){
34588             Roo.applyIf(attr, this.baseAttrs);
34589         }
34590         if(this.applyLoader !== false){
34591             attr.loader = this;
34592         }
34593         // uiProvider = depreciated..
34594         
34595         if(typeof(attr.uiProvider) == 'string'){
34596            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34597                 /**  eval:var:attr */ eval(attr.uiProvider);
34598         }
34599         if(typeof(this.uiProviders['default']) != 'undefined') {
34600             attr.uiProvider = this.uiProviders['default'];
34601         }
34602         
34603         this.fireEvent('create', this, attr);
34604         
34605         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34606         return(attr.leaf ?
34607                         new Roo.tree.TreeNode(attr) :
34608                         new Roo.tree.AsyncTreeNode(attr));
34609     },
34610
34611     processResponse : function(response, node, callback)
34612     {
34613         var json = response.responseText;
34614         try {
34615             
34616             var o = Roo.decode(json);
34617             
34618             if (this.root === false && typeof(o.success) != undefined) {
34619                 this.root = 'data'; // the default behaviour for list like data..
34620                 }
34621                 
34622             if (this.root !== false &&  !o.success) {
34623                 // it's a failure condition.
34624                 var a = response.argument;
34625                 this.fireEvent("loadexception", this, a.node, response);
34626                 Roo.log("Load failed - should have a handler really");
34627                 return;
34628             }
34629             
34630             
34631             
34632             if (this.root !== false) {
34633                  o = o[this.root];
34634             }
34635             
34636             for(var i = 0, len = o.length; i < len; i++){
34637                 var n = this.createNode(o[i]);
34638                 if(n){
34639                     node.appendChild(n);
34640                 }
34641             }
34642             if(typeof callback == "function"){
34643                 callback(this, node);
34644             }
34645         }catch(e){
34646             this.handleFailure(response);
34647         }
34648     },
34649
34650     handleResponse : function(response){
34651         this.transId = false;
34652         var a = response.argument;
34653         this.processResponse(response, a.node, a.callback);
34654         this.fireEvent("load", this, a.node, response);
34655     },
34656
34657     handleFailure : function(response)
34658     {
34659         // should handle failure better..
34660         this.transId = false;
34661         var a = response.argument;
34662         this.fireEvent("loadexception", this, a.node, response);
34663         if(typeof a.callback == "function"){
34664             a.callback(this, a.node);
34665         }
34666     }
34667 });/*
34668  * Based on:
34669  * Ext JS Library 1.1.1
34670  * Copyright(c) 2006-2007, Ext JS, LLC.
34671  *
34672  * Originally Released Under LGPL - original licence link has changed is not relivant.
34673  *
34674  * Fork - LGPL
34675  * <script type="text/javascript">
34676  */
34677
34678 /**
34679 * @class Roo.tree.TreeFilter
34680 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34681 * @param {TreePanel} tree
34682 * @param {Object} config (optional)
34683  */
34684 Roo.tree.TreeFilter = function(tree, config){
34685     this.tree = tree;
34686     this.filtered = {};
34687     Roo.apply(this, config);
34688 };
34689
34690 Roo.tree.TreeFilter.prototype = {
34691     clearBlank:false,
34692     reverse:false,
34693     autoClear:false,
34694     remove:false,
34695
34696      /**
34697      * Filter the data by a specific attribute.
34698      * @param {String/RegExp} value Either string that the attribute value
34699      * should start with or a RegExp to test against the attribute
34700      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34701      * @param {TreeNode} startNode (optional) The node to start the filter at.
34702      */
34703     filter : function(value, attr, startNode){
34704         attr = attr || "text";
34705         var f;
34706         if(typeof value == "string"){
34707             var vlen = value.length;
34708             // auto clear empty filter
34709             if(vlen == 0 && this.clearBlank){
34710                 this.clear();
34711                 return;
34712             }
34713             value = value.toLowerCase();
34714             f = function(n){
34715                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34716             };
34717         }else if(value.exec){ // regex?
34718             f = function(n){
34719                 return value.test(n.attributes[attr]);
34720             };
34721         }else{
34722             throw 'Illegal filter type, must be string or regex';
34723         }
34724         this.filterBy(f, null, startNode);
34725         },
34726
34727     /**
34728      * Filter by a function. The passed function will be called with each
34729      * node in the tree (or from the startNode). If the function returns true, the node is kept
34730      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34731      * @param {Function} fn The filter function
34732      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34733      */
34734     filterBy : function(fn, scope, startNode){
34735         startNode = startNode || this.tree.root;
34736         if(this.autoClear){
34737             this.clear();
34738         }
34739         var af = this.filtered, rv = this.reverse;
34740         var f = function(n){
34741             if(n == startNode){
34742                 return true;
34743             }
34744             if(af[n.id]){
34745                 return false;
34746             }
34747             var m = fn.call(scope || n, n);
34748             if(!m || rv){
34749                 af[n.id] = n;
34750                 n.ui.hide();
34751                 return false;
34752             }
34753             return true;
34754         };
34755         startNode.cascade(f);
34756         if(this.remove){
34757            for(var id in af){
34758                if(typeof id != "function"){
34759                    var n = af[id];
34760                    if(n && n.parentNode){
34761                        n.parentNode.removeChild(n);
34762                    }
34763                }
34764            }
34765         }
34766     },
34767
34768     /**
34769      * Clears the current filter. Note: with the "remove" option
34770      * set a filter cannot be cleared.
34771      */
34772     clear : function(){
34773         var t = this.tree;
34774         var af = this.filtered;
34775         for(var id in af){
34776             if(typeof id != "function"){
34777                 var n = af[id];
34778                 if(n){
34779                     n.ui.show();
34780                 }
34781             }
34782         }
34783         this.filtered = {};
34784     }
34785 };
34786 /*
34787  * Based on:
34788  * Ext JS Library 1.1.1
34789  * Copyright(c) 2006-2007, Ext JS, LLC.
34790  *
34791  * Originally Released Under LGPL - original licence link has changed is not relivant.
34792  *
34793  * Fork - LGPL
34794  * <script type="text/javascript">
34795  */
34796  
34797
34798 /**
34799  * @class Roo.tree.TreeSorter
34800  * Provides sorting of nodes in a TreePanel
34801  * 
34802  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34803  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34804  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34805  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34806  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34807  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34808  * @constructor
34809  * @param {TreePanel} tree
34810  * @param {Object} config
34811  */
34812 Roo.tree.TreeSorter = function(tree, config){
34813     Roo.apply(this, config);
34814     tree.on("beforechildrenrendered", this.doSort, this);
34815     tree.on("append", this.updateSort, this);
34816     tree.on("insert", this.updateSort, this);
34817     
34818     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34819     var p = this.property || "text";
34820     var sortType = this.sortType;
34821     var fs = this.folderSort;
34822     var cs = this.caseSensitive === true;
34823     var leafAttr = this.leafAttr || 'leaf';
34824
34825     this.sortFn = function(n1, n2){
34826         if(fs){
34827             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34828                 return 1;
34829             }
34830             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34831                 return -1;
34832             }
34833         }
34834         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34835         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34836         if(v1 < v2){
34837                         return dsc ? +1 : -1;
34838                 }else if(v1 > v2){
34839                         return dsc ? -1 : +1;
34840         }else{
34841                 return 0;
34842         }
34843     };
34844 };
34845
34846 Roo.tree.TreeSorter.prototype = {
34847     doSort : function(node){
34848         node.sort(this.sortFn);
34849     },
34850     
34851     compareNodes : function(n1, n2){
34852         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34853     },
34854     
34855     updateSort : function(tree, node){
34856         if(node.childrenRendered){
34857             this.doSort.defer(1, this, [node]);
34858         }
34859     }
34860 };/*
34861  * Based on:
34862  * Ext JS Library 1.1.1
34863  * Copyright(c) 2006-2007, Ext JS, LLC.
34864  *
34865  * Originally Released Under LGPL - original licence link has changed is not relivant.
34866  *
34867  * Fork - LGPL
34868  * <script type="text/javascript">
34869  */
34870
34871 if(Roo.dd.DropZone){
34872     
34873 Roo.tree.TreeDropZone = function(tree, config){
34874     this.allowParentInsert = false;
34875     this.allowContainerDrop = false;
34876     this.appendOnly = false;
34877     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34878     this.tree = tree;
34879     this.lastInsertClass = "x-tree-no-status";
34880     this.dragOverData = {};
34881 };
34882
34883 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34884     ddGroup : "TreeDD",
34885     scroll:  true,
34886     
34887     expandDelay : 1000,
34888     
34889     expandNode : function(node){
34890         if(node.hasChildNodes() && !node.isExpanded()){
34891             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34892         }
34893     },
34894     
34895     queueExpand : function(node){
34896         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34897     },
34898     
34899     cancelExpand : function(){
34900         if(this.expandProcId){
34901             clearTimeout(this.expandProcId);
34902             this.expandProcId = false;
34903         }
34904     },
34905     
34906     isValidDropPoint : function(n, pt, dd, e, data){
34907         if(!n || !data){ return false; }
34908         var targetNode = n.node;
34909         var dropNode = data.node;
34910         // default drop rules
34911         if(!(targetNode && targetNode.isTarget && pt)){
34912             return false;
34913         }
34914         if(pt == "append" && targetNode.allowChildren === false){
34915             return false;
34916         }
34917         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34918             return false;
34919         }
34920         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34921             return false;
34922         }
34923         // reuse the object
34924         var overEvent = this.dragOverData;
34925         overEvent.tree = this.tree;
34926         overEvent.target = targetNode;
34927         overEvent.data = data;
34928         overEvent.point = pt;
34929         overEvent.source = dd;
34930         overEvent.rawEvent = e;
34931         overEvent.dropNode = dropNode;
34932         overEvent.cancel = false;  
34933         var result = this.tree.fireEvent("nodedragover", overEvent);
34934         return overEvent.cancel === false && result !== false;
34935     },
34936     
34937     getDropPoint : function(e, n, dd)
34938     {
34939         var tn = n.node;
34940         if(tn.isRoot){
34941             return tn.allowChildren !== false ? "append" : false; // always append for root
34942         }
34943         var dragEl = n.ddel;
34944         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34945         var y = Roo.lib.Event.getPageY(e);
34946         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34947         
34948         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34949         var noAppend = tn.allowChildren === false;
34950         if(this.appendOnly || tn.parentNode.allowChildren === false){
34951             return noAppend ? false : "append";
34952         }
34953         var noBelow = false;
34954         if(!this.allowParentInsert){
34955             noBelow = tn.hasChildNodes() && tn.isExpanded();
34956         }
34957         var q = (b - t) / (noAppend ? 2 : 3);
34958         if(y >= t && y < (t + q)){
34959             return "above";
34960         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34961             return "below";
34962         }else{
34963             return "append";
34964         }
34965     },
34966     
34967     onNodeEnter : function(n, dd, e, data)
34968     {
34969         this.cancelExpand();
34970     },
34971     
34972     onNodeOver : function(n, dd, e, data)
34973     {
34974        
34975         var pt = this.getDropPoint(e, n, dd);
34976         var node = n.node;
34977         
34978         // auto node expand check
34979         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34980             this.queueExpand(node);
34981         }else if(pt != "append"){
34982             this.cancelExpand();
34983         }
34984         
34985         // set the insert point style on the target node
34986         var returnCls = this.dropNotAllowed;
34987         if(this.isValidDropPoint(n, pt, dd, e, data)){
34988            if(pt){
34989                var el = n.ddel;
34990                var cls;
34991                if(pt == "above"){
34992                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34993                    cls = "x-tree-drag-insert-above";
34994                }else if(pt == "below"){
34995                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34996                    cls = "x-tree-drag-insert-below";
34997                }else{
34998                    returnCls = "x-tree-drop-ok-append";
34999                    cls = "x-tree-drag-append";
35000                }
35001                if(this.lastInsertClass != cls){
35002                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
35003                    this.lastInsertClass = cls;
35004                }
35005            }
35006        }
35007        return returnCls;
35008     },
35009     
35010     onNodeOut : function(n, dd, e, data){
35011         
35012         this.cancelExpand();
35013         this.removeDropIndicators(n);
35014     },
35015     
35016     onNodeDrop : function(n, dd, e, data){
35017         var point = this.getDropPoint(e, n, dd);
35018         var targetNode = n.node;
35019         targetNode.ui.startDrop();
35020         if(!this.isValidDropPoint(n, point, dd, e, data)){
35021             targetNode.ui.endDrop();
35022             return false;
35023         }
35024         // first try to find the drop node
35025         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
35026         var dropEvent = {
35027             tree : this.tree,
35028             target: targetNode,
35029             data: data,
35030             point: point,
35031             source: dd,
35032             rawEvent: e,
35033             dropNode: dropNode,
35034             cancel: !dropNode   
35035         };
35036         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35037         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35038             targetNode.ui.endDrop();
35039             return false;
35040         }
35041         // allow target changing
35042         targetNode = dropEvent.target;
35043         if(point == "append" && !targetNode.isExpanded()){
35044             targetNode.expand(false, null, function(){
35045                 this.completeDrop(dropEvent);
35046             }.createDelegate(this));
35047         }else{
35048             this.completeDrop(dropEvent);
35049         }
35050         return true;
35051     },
35052     
35053     completeDrop : function(de){
35054         var ns = de.dropNode, p = de.point, t = de.target;
35055         if(!(ns instanceof Array)){
35056             ns = [ns];
35057         }
35058         var n;
35059         for(var i = 0, len = ns.length; i < len; i++){
35060             n = ns[i];
35061             if(p == "above"){
35062                 t.parentNode.insertBefore(n, t);
35063             }else if(p == "below"){
35064                 t.parentNode.insertBefore(n, t.nextSibling);
35065             }else{
35066                 t.appendChild(n);
35067             }
35068         }
35069         n.ui.focus();
35070         if(this.tree.hlDrop){
35071             n.ui.highlight();
35072         }
35073         t.ui.endDrop();
35074         this.tree.fireEvent("nodedrop", de);
35075     },
35076     
35077     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35078         if(this.tree.hlDrop){
35079             dropNode.ui.focus();
35080             dropNode.ui.highlight();
35081         }
35082         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35083     },
35084     
35085     getTree : function(){
35086         return this.tree;
35087     },
35088     
35089     removeDropIndicators : function(n){
35090         if(n && n.ddel){
35091             var el = n.ddel;
35092             Roo.fly(el).removeClass([
35093                     "x-tree-drag-insert-above",
35094                     "x-tree-drag-insert-below",
35095                     "x-tree-drag-append"]);
35096             this.lastInsertClass = "_noclass";
35097         }
35098     },
35099     
35100     beforeDragDrop : function(target, e, id){
35101         this.cancelExpand();
35102         return true;
35103     },
35104     
35105     afterRepair : function(data){
35106         if(data && Roo.enableFx){
35107             data.node.ui.highlight();
35108         }
35109         this.hideProxy();
35110     } 
35111     
35112 });
35113
35114 }
35115 /*
35116  * Based on:
35117  * Ext JS Library 1.1.1
35118  * Copyright(c) 2006-2007, Ext JS, LLC.
35119  *
35120  * Originally Released Under LGPL - original licence link has changed is not relivant.
35121  *
35122  * Fork - LGPL
35123  * <script type="text/javascript">
35124  */
35125  
35126
35127 if(Roo.dd.DragZone){
35128 Roo.tree.TreeDragZone = function(tree, config){
35129     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35130     this.tree = tree;
35131 };
35132
35133 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35134     ddGroup : "TreeDD",
35135    
35136     onBeforeDrag : function(data, e){
35137         var n = data.node;
35138         return n && n.draggable && !n.disabled;
35139     },
35140      
35141     
35142     onInitDrag : function(e){
35143         var data = this.dragData;
35144         this.tree.getSelectionModel().select(data.node);
35145         this.proxy.update("");
35146         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35147         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35148     },
35149     
35150     getRepairXY : function(e, data){
35151         return data.node.ui.getDDRepairXY();
35152     },
35153     
35154     onEndDrag : function(data, e){
35155         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35156         
35157         
35158     },
35159     
35160     onValidDrop : function(dd, e, id){
35161         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35162         this.hideProxy();
35163     },
35164     
35165     beforeInvalidDrop : function(e, id){
35166         // this scrolls the original position back into view
35167         var sm = this.tree.getSelectionModel();
35168         sm.clearSelections();
35169         sm.select(this.dragData.node);
35170     }
35171 });
35172 }/*
35173  * Based on:
35174  * Ext JS Library 1.1.1
35175  * Copyright(c) 2006-2007, Ext JS, LLC.
35176  *
35177  * Originally Released Under LGPL - original licence link has changed is not relivant.
35178  *
35179  * Fork - LGPL
35180  * <script type="text/javascript">
35181  */
35182 /**
35183  * @class Roo.tree.TreeEditor
35184  * @extends Roo.Editor
35185  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35186  * as the editor field.
35187  * @constructor
35188  * @param {Object} config (used to be the tree panel.)
35189  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35190  * 
35191  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35192  * @cfg {Roo.form.TextField|Object} field The field configuration
35193  *
35194  * 
35195  */
35196 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35197     var tree = config;
35198     var field;
35199     if (oldconfig) { // old style..
35200         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35201     } else {
35202         // new style..
35203         tree = config.tree;
35204         config.field = config.field  || {};
35205         config.field.xtype = 'TextField';
35206         field = Roo.factory(config.field, Roo.form);
35207     }
35208     config = config || {};
35209     
35210     
35211     this.addEvents({
35212         /**
35213          * @event beforenodeedit
35214          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35215          * false from the handler of this event.
35216          * @param {Editor} this
35217          * @param {Roo.tree.Node} node 
35218          */
35219         "beforenodeedit" : true
35220     });
35221     
35222     //Roo.log(config);
35223     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35224
35225     this.tree = tree;
35226
35227     tree.on('beforeclick', this.beforeNodeClick, this);
35228     tree.getTreeEl().on('mousedown', this.hide, this);
35229     this.on('complete', this.updateNode, this);
35230     this.on('beforestartedit', this.fitToTree, this);
35231     this.on('startedit', this.bindScroll, this, {delay:10});
35232     this.on('specialkey', this.onSpecialKey, this);
35233 };
35234
35235 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35236     /**
35237      * @cfg {String} alignment
35238      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35239      */
35240     alignment: "l-l",
35241     // inherit
35242     autoSize: false,
35243     /**
35244      * @cfg {Boolean} hideEl
35245      * True to hide the bound element while the editor is displayed (defaults to false)
35246      */
35247     hideEl : false,
35248     /**
35249      * @cfg {String} cls
35250      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35251      */
35252     cls: "x-small-editor x-tree-editor",
35253     /**
35254      * @cfg {Boolean} shim
35255      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35256      */
35257     shim:false,
35258     // inherit
35259     shadow:"frame",
35260     /**
35261      * @cfg {Number} maxWidth
35262      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35263      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35264      * scroll and client offsets into account prior to each edit.
35265      */
35266     maxWidth: 250,
35267
35268     editDelay : 350,
35269
35270     // private
35271     fitToTree : function(ed, el){
35272         var td = this.tree.getTreeEl().dom, nd = el.dom;
35273         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35274             td.scrollLeft = nd.offsetLeft;
35275         }
35276         var w = Math.min(
35277                 this.maxWidth,
35278                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35279         this.setSize(w, '');
35280         
35281         return this.fireEvent('beforenodeedit', this, this.editNode);
35282         
35283     },
35284
35285     // private
35286     triggerEdit : function(node){
35287         this.completeEdit();
35288         this.editNode = node;
35289         this.startEdit(node.ui.textNode, node.text);
35290     },
35291
35292     // private
35293     bindScroll : function(){
35294         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35295     },
35296
35297     // private
35298     beforeNodeClick : function(node, e){
35299         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35300         this.lastClick = new Date();
35301         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35302             e.stopEvent();
35303             this.triggerEdit(node);
35304             return false;
35305         }
35306         return true;
35307     },
35308
35309     // private
35310     updateNode : function(ed, value){
35311         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35312         this.editNode.setText(value);
35313     },
35314
35315     // private
35316     onHide : function(){
35317         Roo.tree.TreeEditor.superclass.onHide.call(this);
35318         if(this.editNode){
35319             this.editNode.ui.focus();
35320         }
35321     },
35322
35323     // private
35324     onSpecialKey : function(field, e){
35325         var k = e.getKey();
35326         if(k == e.ESC){
35327             e.stopEvent();
35328             this.cancelEdit();
35329         }else if(k == e.ENTER && !e.hasModifier()){
35330             e.stopEvent();
35331             this.completeEdit();
35332         }
35333     }
35334 });//<Script type="text/javascript">
35335 /*
35336  * Based on:
35337  * Ext JS Library 1.1.1
35338  * Copyright(c) 2006-2007, Ext JS, LLC.
35339  *
35340  * Originally Released Under LGPL - original licence link has changed is not relivant.
35341  *
35342  * Fork - LGPL
35343  * <script type="text/javascript">
35344  */
35345  
35346 /**
35347  * Not documented??? - probably should be...
35348  */
35349
35350 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35351     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35352     
35353     renderElements : function(n, a, targetNode, bulkRender){
35354         //consel.log("renderElements?");
35355         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35356
35357         var t = n.getOwnerTree();
35358         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35359         
35360         var cols = t.columns;
35361         var bw = t.borderWidth;
35362         var c = cols[0];
35363         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35364          var cb = typeof a.checked == "boolean";
35365         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35366         var colcls = 'x-t-' + tid + '-c0';
35367         var buf = [
35368             '<li class="x-tree-node">',
35369             
35370                 
35371                 '<div class="x-tree-node-el ', a.cls,'">',
35372                     // extran...
35373                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35374                 
35375                 
35376                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35377                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35378                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35379                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35380                            (a.iconCls ? ' '+a.iconCls : ''),
35381                            '" unselectable="on" />',
35382                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35383                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35384                              
35385                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35386                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35387                             '<span unselectable="on" qtip="' + tx + '">',
35388                              tx,
35389                              '</span></a>' ,
35390                     '</div>',
35391                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35392                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35393                  ];
35394         for(var i = 1, len = cols.length; i < len; i++){
35395             c = cols[i];
35396             colcls = 'x-t-' + tid + '-c' +i;
35397             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35398             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35399                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35400                       "</div>");
35401          }
35402          
35403          buf.push(
35404             '</a>',
35405             '<div class="x-clear"></div></div>',
35406             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35407             "</li>");
35408         
35409         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35410             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35411                                 n.nextSibling.ui.getEl(), buf.join(""));
35412         }else{
35413             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35414         }
35415         var el = this.wrap.firstChild;
35416         this.elRow = el;
35417         this.elNode = el.firstChild;
35418         this.ranchor = el.childNodes[1];
35419         this.ctNode = this.wrap.childNodes[1];
35420         var cs = el.firstChild.childNodes;
35421         this.indentNode = cs[0];
35422         this.ecNode = cs[1];
35423         this.iconNode = cs[2];
35424         var index = 3;
35425         if(cb){
35426             this.checkbox = cs[3];
35427             index++;
35428         }
35429         this.anchor = cs[index];
35430         
35431         this.textNode = cs[index].firstChild;
35432         
35433         //el.on("click", this.onClick, this);
35434         //el.on("dblclick", this.onDblClick, this);
35435         
35436         
35437        // console.log(this);
35438     },
35439     initEvents : function(){
35440         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35441         
35442             
35443         var a = this.ranchor;
35444
35445         var el = Roo.get(a);
35446
35447         if(Roo.isOpera){ // opera render bug ignores the CSS
35448             el.setStyle("text-decoration", "none");
35449         }
35450
35451         el.on("click", this.onClick, this);
35452         el.on("dblclick", this.onDblClick, this);
35453         el.on("contextmenu", this.onContextMenu, this);
35454         
35455     },
35456     
35457     /*onSelectedChange : function(state){
35458         if(state){
35459             this.focus();
35460             this.addClass("x-tree-selected");
35461         }else{
35462             //this.blur();
35463             this.removeClass("x-tree-selected");
35464         }
35465     },*/
35466     addClass : function(cls){
35467         if(this.elRow){
35468             Roo.fly(this.elRow).addClass(cls);
35469         }
35470         
35471     },
35472     
35473     
35474     removeClass : function(cls){
35475         if(this.elRow){
35476             Roo.fly(this.elRow).removeClass(cls);
35477         }
35478     }
35479
35480     
35481     
35482 });//<Script type="text/javascript">
35483
35484 /*
35485  * Based on:
35486  * Ext JS Library 1.1.1
35487  * Copyright(c) 2006-2007, Ext JS, LLC.
35488  *
35489  * Originally Released Under LGPL - original licence link has changed is not relivant.
35490  *
35491  * Fork - LGPL
35492  * <script type="text/javascript">
35493  */
35494  
35495
35496 /**
35497  * @class Roo.tree.ColumnTree
35498  * @extends Roo.data.TreePanel
35499  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35500  * @cfg {int} borderWidth  compined right/left border allowance
35501  * @constructor
35502  * @param {String/HTMLElement/Element} el The container element
35503  * @param {Object} config
35504  */
35505 Roo.tree.ColumnTree =  function(el, config)
35506 {
35507    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35508    this.addEvents({
35509         /**
35510         * @event resize
35511         * Fire this event on a container when it resizes
35512         * @param {int} w Width
35513         * @param {int} h Height
35514         */
35515        "resize" : true
35516     });
35517     this.on('resize', this.onResize, this);
35518 };
35519
35520 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35521     //lines:false,
35522     
35523     
35524     borderWidth: Roo.isBorderBox ? 0 : 2, 
35525     headEls : false,
35526     
35527     render : function(){
35528         // add the header.....
35529        
35530         Roo.tree.ColumnTree.superclass.render.apply(this);
35531         
35532         this.el.addClass('x-column-tree');
35533         
35534         this.headers = this.el.createChild(
35535             {cls:'x-tree-headers'},this.innerCt.dom);
35536    
35537         var cols = this.columns, c;
35538         var totalWidth = 0;
35539         this.headEls = [];
35540         var  len = cols.length;
35541         for(var i = 0; i < len; i++){
35542              c = cols[i];
35543              totalWidth += c.width;
35544             this.headEls.push(this.headers.createChild({
35545                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35546                  cn: {
35547                      cls:'x-tree-hd-text',
35548                      html: c.header
35549                  },
35550                  style:'width:'+(c.width-this.borderWidth)+'px;'
35551              }));
35552         }
35553         this.headers.createChild({cls:'x-clear'});
35554         // prevent floats from wrapping when clipped
35555         this.headers.setWidth(totalWidth);
35556         //this.innerCt.setWidth(totalWidth);
35557         this.innerCt.setStyle({ overflow: 'auto' });
35558         this.onResize(this.width, this.height);
35559              
35560         
35561     },
35562     onResize : function(w,h)
35563     {
35564         this.height = h;
35565         this.width = w;
35566         // resize cols..
35567         this.innerCt.setWidth(this.width);
35568         this.innerCt.setHeight(this.height-20);
35569         
35570         // headers...
35571         var cols = this.columns, c;
35572         var totalWidth = 0;
35573         var expEl = false;
35574         var len = cols.length;
35575         for(var i = 0; i < len; i++){
35576             c = cols[i];
35577             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35578                 // it's the expander..
35579                 expEl  = this.headEls[i];
35580                 continue;
35581             }
35582             totalWidth += c.width;
35583             
35584         }
35585         if (expEl) {
35586             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35587         }
35588         this.headers.setWidth(w-20);
35589
35590         
35591         
35592         
35593     }
35594 });
35595 /*
35596  * Based on:
35597  * Ext JS Library 1.1.1
35598  * Copyright(c) 2006-2007, Ext JS, LLC.
35599  *
35600  * Originally Released Under LGPL - original licence link has changed is not relivant.
35601  *
35602  * Fork - LGPL
35603  * <script type="text/javascript">
35604  */
35605  
35606 /**
35607  * @class Roo.menu.Menu
35608  * @extends Roo.util.Observable
35609  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35610  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35611  * @constructor
35612  * Creates a new Menu
35613  * @param {Object} config Configuration options
35614  */
35615 Roo.menu.Menu = function(config){
35616     Roo.apply(this, config);
35617     this.id = this.id || Roo.id();
35618     this.addEvents({
35619         /**
35620          * @event beforeshow
35621          * Fires before this menu is displayed
35622          * @param {Roo.menu.Menu} this
35623          */
35624         beforeshow : true,
35625         /**
35626          * @event beforehide
35627          * Fires before this menu is hidden
35628          * @param {Roo.menu.Menu} this
35629          */
35630         beforehide : true,
35631         /**
35632          * @event show
35633          * Fires after this menu is displayed
35634          * @param {Roo.menu.Menu} this
35635          */
35636         show : true,
35637         /**
35638          * @event hide
35639          * Fires after this menu is hidden
35640          * @param {Roo.menu.Menu} this
35641          */
35642         hide : true,
35643         /**
35644          * @event click
35645          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35646          * @param {Roo.menu.Menu} this
35647          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35648          * @param {Roo.EventObject} e
35649          */
35650         click : true,
35651         /**
35652          * @event mouseover
35653          * Fires when the mouse is hovering over this menu
35654          * @param {Roo.menu.Menu} this
35655          * @param {Roo.EventObject} e
35656          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35657          */
35658         mouseover : true,
35659         /**
35660          * @event mouseout
35661          * Fires when the mouse exits this menu
35662          * @param {Roo.menu.Menu} this
35663          * @param {Roo.EventObject} e
35664          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35665          */
35666         mouseout : true,
35667         /**
35668          * @event itemclick
35669          * Fires when a menu item contained in this menu is clicked
35670          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35671          * @param {Roo.EventObject} e
35672          */
35673         itemclick: true
35674     });
35675     if (this.registerMenu) {
35676         Roo.menu.MenuMgr.register(this);
35677     }
35678     
35679     var mis = this.items;
35680     this.items = new Roo.util.MixedCollection();
35681     if(mis){
35682         this.add.apply(this, mis);
35683     }
35684 };
35685
35686 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35687     /**
35688      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35689      */
35690     minWidth : 120,
35691     /**
35692      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35693      * for bottom-right shadow (defaults to "sides")
35694      */
35695     shadow : "sides",
35696     /**
35697      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35698      * this menu (defaults to "tl-tr?")
35699      */
35700     subMenuAlign : "tl-tr?",
35701     /**
35702      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35703      * relative to its element of origin (defaults to "tl-bl?")
35704      */
35705     defaultAlign : "tl-bl?",
35706     /**
35707      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35708      */
35709     allowOtherMenus : false,
35710     /**
35711      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35712      */
35713     registerMenu : true,
35714
35715     hidden:true,
35716
35717     // private
35718     render : function(){
35719         if(this.el){
35720             return;
35721         }
35722         var el = this.el = new Roo.Layer({
35723             cls: "x-menu",
35724             shadow:this.shadow,
35725             constrain: false,
35726             parentEl: this.parentEl || document.body,
35727             zindex:15000
35728         });
35729
35730         this.keyNav = new Roo.menu.MenuNav(this);
35731
35732         if(this.plain){
35733             el.addClass("x-menu-plain");
35734         }
35735         if(this.cls){
35736             el.addClass(this.cls);
35737         }
35738         // generic focus element
35739         this.focusEl = el.createChild({
35740             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35741         });
35742         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35743         //disabling touch- as it's causing issues ..
35744         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35745         ul.on('click'   , this.onClick, this);
35746         
35747         
35748         ul.on("mouseover", this.onMouseOver, this);
35749         ul.on("mouseout", this.onMouseOut, this);
35750         this.items.each(function(item){
35751             if (item.hidden) {
35752                 return;
35753             }
35754             
35755             var li = document.createElement("li");
35756             li.className = "x-menu-list-item";
35757             ul.dom.appendChild(li);
35758             item.render(li, this);
35759         }, this);
35760         this.ul = ul;
35761         this.autoWidth();
35762     },
35763
35764     // private
35765     autoWidth : function(){
35766         var el = this.el, ul = this.ul;
35767         if(!el){
35768             return;
35769         }
35770         var w = this.width;
35771         if(w){
35772             el.setWidth(w);
35773         }else if(Roo.isIE){
35774             el.setWidth(this.minWidth);
35775             var t = el.dom.offsetWidth; // force recalc
35776             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35777         }
35778     },
35779
35780     // private
35781     delayAutoWidth : function(){
35782         if(this.rendered){
35783             if(!this.awTask){
35784                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35785             }
35786             this.awTask.delay(20);
35787         }
35788     },
35789
35790     // private
35791     findTargetItem : function(e){
35792         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35793         if(t && t.menuItemId){
35794             return this.items.get(t.menuItemId);
35795         }
35796     },
35797
35798     // private
35799     onClick : function(e){
35800         Roo.log("menu.onClick");
35801         var t = this.findTargetItem(e);
35802         if(!t){
35803             return;
35804         }
35805         Roo.log(e);
35806         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35807             if(t == this.activeItem && t.shouldDeactivate(e)){
35808                 this.activeItem.deactivate();
35809                 delete this.activeItem;
35810                 return;
35811             }
35812             if(t.canActivate){
35813                 this.setActiveItem(t, true);
35814             }
35815             return;
35816             
35817             
35818         }
35819         
35820         t.onClick(e);
35821         this.fireEvent("click", this, t, e);
35822     },
35823
35824     // private
35825     setActiveItem : function(item, autoExpand){
35826         if(item != this.activeItem){
35827             if(this.activeItem){
35828                 this.activeItem.deactivate();
35829             }
35830             this.activeItem = item;
35831             item.activate(autoExpand);
35832         }else if(autoExpand){
35833             item.expandMenu();
35834         }
35835     },
35836
35837     // private
35838     tryActivate : function(start, step){
35839         var items = this.items;
35840         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35841             var item = items.get(i);
35842             if(!item.disabled && item.canActivate){
35843                 this.setActiveItem(item, false);
35844                 return item;
35845             }
35846         }
35847         return false;
35848     },
35849
35850     // private
35851     onMouseOver : function(e){
35852         var t;
35853         if(t = this.findTargetItem(e)){
35854             if(t.canActivate && !t.disabled){
35855                 this.setActiveItem(t, true);
35856             }
35857         }
35858         this.fireEvent("mouseover", this, e, t);
35859     },
35860
35861     // private
35862     onMouseOut : function(e){
35863         var t;
35864         if(t = this.findTargetItem(e)){
35865             if(t == this.activeItem && t.shouldDeactivate(e)){
35866                 this.activeItem.deactivate();
35867                 delete this.activeItem;
35868             }
35869         }
35870         this.fireEvent("mouseout", this, e, t);
35871     },
35872
35873     /**
35874      * Read-only.  Returns true if the menu is currently displayed, else false.
35875      * @type Boolean
35876      */
35877     isVisible : function(){
35878         return this.el && !this.hidden;
35879     },
35880
35881     /**
35882      * Displays this menu relative to another element
35883      * @param {String/HTMLElement/Roo.Element} element The element to align to
35884      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35885      * the element (defaults to this.defaultAlign)
35886      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35887      */
35888     show : function(el, pos, parentMenu){
35889         this.parentMenu = parentMenu;
35890         if(!this.el){
35891             this.render();
35892         }
35893         this.fireEvent("beforeshow", this);
35894         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35895     },
35896
35897     /**
35898      * Displays this menu at a specific xy position
35899      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35900      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35901      */
35902     showAt : function(xy, parentMenu, /* private: */_e){
35903         this.parentMenu = parentMenu;
35904         if(!this.el){
35905             this.render();
35906         }
35907         if(_e !== false){
35908             this.fireEvent("beforeshow", this);
35909             xy = this.el.adjustForConstraints(xy);
35910         }
35911         this.el.setXY(xy);
35912         this.el.show();
35913         this.hidden = false;
35914         this.focus();
35915         this.fireEvent("show", this);
35916     },
35917
35918     focus : function(){
35919         if(!this.hidden){
35920             this.doFocus.defer(50, this);
35921         }
35922     },
35923
35924     doFocus : function(){
35925         if(!this.hidden){
35926             this.focusEl.focus();
35927         }
35928     },
35929
35930     /**
35931      * Hides this menu and optionally all parent menus
35932      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35933      */
35934     hide : function(deep){
35935         if(this.el && this.isVisible()){
35936             this.fireEvent("beforehide", this);
35937             if(this.activeItem){
35938                 this.activeItem.deactivate();
35939                 this.activeItem = null;
35940             }
35941             this.el.hide();
35942             this.hidden = true;
35943             this.fireEvent("hide", this);
35944         }
35945         if(deep === true && this.parentMenu){
35946             this.parentMenu.hide(true);
35947         }
35948     },
35949
35950     /**
35951      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35952      * Any of the following are valid:
35953      * <ul>
35954      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35955      * <li>An HTMLElement object which will be converted to a menu item</li>
35956      * <li>A menu item config object that will be created as a new menu item</li>
35957      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35958      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35959      * </ul>
35960      * Usage:
35961      * <pre><code>
35962 // Create the menu
35963 var menu = new Roo.menu.Menu();
35964
35965 // Create a menu item to add by reference
35966 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35967
35968 // Add a bunch of items at once using different methods.
35969 // Only the last item added will be returned.
35970 var item = menu.add(
35971     menuItem,                // add existing item by ref
35972     'Dynamic Item',          // new TextItem
35973     '-',                     // new separator
35974     { text: 'Config Item' }  // new item by config
35975 );
35976 </code></pre>
35977      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35978      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35979      */
35980     add : function(){
35981         var a = arguments, l = a.length, item;
35982         for(var i = 0; i < l; i++){
35983             var el = a[i];
35984             if ((typeof(el) == "object") && el.xtype && el.xns) {
35985                 el = Roo.factory(el, Roo.menu);
35986             }
35987             
35988             if(el.render){ // some kind of Item
35989                 item = this.addItem(el);
35990             }else if(typeof el == "string"){ // string
35991                 if(el == "separator" || el == "-"){
35992                     item = this.addSeparator();
35993                 }else{
35994                     item = this.addText(el);
35995                 }
35996             }else if(el.tagName || el.el){ // element
35997                 item = this.addElement(el);
35998             }else if(typeof el == "object"){ // must be menu item config?
35999                 item = this.addMenuItem(el);
36000             }
36001         }
36002         return item;
36003     },
36004
36005     /**
36006      * Returns this menu's underlying {@link Roo.Element} object
36007      * @return {Roo.Element} The element
36008      */
36009     getEl : function(){
36010         if(!this.el){
36011             this.render();
36012         }
36013         return this.el;
36014     },
36015
36016     /**
36017      * Adds a separator bar to the menu
36018      * @return {Roo.menu.Item} The menu item that was added
36019      */
36020     addSeparator : function(){
36021         return this.addItem(new Roo.menu.Separator());
36022     },
36023
36024     /**
36025      * Adds an {@link Roo.Element} object to the menu
36026      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
36027      * @return {Roo.menu.Item} The menu item that was added
36028      */
36029     addElement : function(el){
36030         return this.addItem(new Roo.menu.BaseItem(el));
36031     },
36032
36033     /**
36034      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36035      * @param {Roo.menu.Item} item The menu item to add
36036      * @return {Roo.menu.Item} The menu item that was added
36037      */
36038     addItem : function(item){
36039         this.items.add(item);
36040         if(this.ul){
36041             var li = document.createElement("li");
36042             li.className = "x-menu-list-item";
36043             this.ul.dom.appendChild(li);
36044             item.render(li, this);
36045             this.delayAutoWidth();
36046         }
36047         return item;
36048     },
36049
36050     /**
36051      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36052      * @param {Object} config A MenuItem config object
36053      * @return {Roo.menu.Item} The menu item that was added
36054      */
36055     addMenuItem : function(config){
36056         if(!(config instanceof Roo.menu.Item)){
36057             if(typeof config.checked == "boolean"){ // must be check menu item config?
36058                 config = new Roo.menu.CheckItem(config);
36059             }else{
36060                 config = new Roo.menu.Item(config);
36061             }
36062         }
36063         return this.addItem(config);
36064     },
36065
36066     /**
36067      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36068      * @param {String} text The text to display in the menu item
36069      * @return {Roo.menu.Item} The menu item that was added
36070      */
36071     addText : function(text){
36072         return this.addItem(new Roo.menu.TextItem({ text : text }));
36073     },
36074
36075     /**
36076      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36077      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36078      * @param {Roo.menu.Item} item The menu item to add
36079      * @return {Roo.menu.Item} The menu item that was added
36080      */
36081     insert : function(index, item){
36082         this.items.insert(index, item);
36083         if(this.ul){
36084             var li = document.createElement("li");
36085             li.className = "x-menu-list-item";
36086             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36087             item.render(li, this);
36088             this.delayAutoWidth();
36089         }
36090         return item;
36091     },
36092
36093     /**
36094      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36095      * @param {Roo.menu.Item} item The menu item to remove
36096      */
36097     remove : function(item){
36098         this.items.removeKey(item.id);
36099         item.destroy();
36100     },
36101
36102     /**
36103      * Removes and destroys all items in the menu
36104      */
36105     removeAll : function(){
36106         var f;
36107         while(f = this.items.first()){
36108             this.remove(f);
36109         }
36110     }
36111 });
36112
36113 // MenuNav is a private utility class used internally by the Menu
36114 Roo.menu.MenuNav = function(menu){
36115     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36116     this.scope = this.menu = menu;
36117 };
36118
36119 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36120     doRelay : function(e, h){
36121         var k = e.getKey();
36122         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36123             this.menu.tryActivate(0, 1);
36124             return false;
36125         }
36126         return h.call(this.scope || this, e, this.menu);
36127     },
36128
36129     up : function(e, m){
36130         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36131             m.tryActivate(m.items.length-1, -1);
36132         }
36133     },
36134
36135     down : function(e, m){
36136         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36137             m.tryActivate(0, 1);
36138         }
36139     },
36140
36141     right : function(e, m){
36142         if(m.activeItem){
36143             m.activeItem.expandMenu(true);
36144         }
36145     },
36146
36147     left : function(e, m){
36148         m.hide();
36149         if(m.parentMenu && m.parentMenu.activeItem){
36150             m.parentMenu.activeItem.activate();
36151         }
36152     },
36153
36154     enter : function(e, m){
36155         if(m.activeItem){
36156             e.stopPropagation();
36157             m.activeItem.onClick(e);
36158             m.fireEvent("click", this, m.activeItem);
36159             return true;
36160         }
36161     }
36162 });/*
36163  * Based on:
36164  * Ext JS Library 1.1.1
36165  * Copyright(c) 2006-2007, Ext JS, LLC.
36166  *
36167  * Originally Released Under LGPL - original licence link has changed is not relivant.
36168  *
36169  * Fork - LGPL
36170  * <script type="text/javascript">
36171  */
36172  
36173 /**
36174  * @class Roo.menu.MenuMgr
36175  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36176  * @singleton
36177  */
36178 Roo.menu.MenuMgr = function(){
36179    var menus, active, groups = {}, attached = false, lastShow = new Date();
36180
36181    // private - called when first menu is created
36182    function init(){
36183        menus = {};
36184        active = new Roo.util.MixedCollection();
36185        Roo.get(document).addKeyListener(27, function(){
36186            if(active.length > 0){
36187                hideAll();
36188            }
36189        });
36190    }
36191
36192    // private
36193    function hideAll(){
36194        if(active && active.length > 0){
36195            var c = active.clone();
36196            c.each(function(m){
36197                m.hide();
36198            });
36199        }
36200    }
36201
36202    // private
36203    function onHide(m){
36204        active.remove(m);
36205        if(active.length < 1){
36206            Roo.get(document).un("mousedown", onMouseDown);
36207            attached = false;
36208        }
36209    }
36210
36211    // private
36212    function onShow(m){
36213        var last = active.last();
36214        lastShow = new Date();
36215        active.add(m);
36216        if(!attached){
36217            Roo.get(document).on("mousedown", onMouseDown);
36218            attached = true;
36219        }
36220        if(m.parentMenu){
36221           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36222           m.parentMenu.activeChild = m;
36223        }else if(last && last.isVisible()){
36224           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36225        }
36226    }
36227
36228    // private
36229    function onBeforeHide(m){
36230        if(m.activeChild){
36231            m.activeChild.hide();
36232        }
36233        if(m.autoHideTimer){
36234            clearTimeout(m.autoHideTimer);
36235            delete m.autoHideTimer;
36236        }
36237    }
36238
36239    // private
36240    function onBeforeShow(m){
36241        var pm = m.parentMenu;
36242        if(!pm && !m.allowOtherMenus){
36243            hideAll();
36244        }else if(pm && pm.activeChild && active != m){
36245            pm.activeChild.hide();
36246        }
36247    }
36248
36249    // private
36250    function onMouseDown(e){
36251        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36252            hideAll();
36253        }
36254    }
36255
36256    // private
36257    function onBeforeCheck(mi, state){
36258        if(state){
36259            var g = groups[mi.group];
36260            for(var i = 0, l = g.length; i < l; i++){
36261                if(g[i] != mi){
36262                    g[i].setChecked(false);
36263                }
36264            }
36265        }
36266    }
36267
36268    return {
36269
36270        /**
36271         * Hides all menus that are currently visible
36272         */
36273        hideAll : function(){
36274             hideAll();  
36275        },
36276
36277        // private
36278        register : function(menu){
36279            if(!menus){
36280                init();
36281            }
36282            menus[menu.id] = menu;
36283            menu.on("beforehide", onBeforeHide);
36284            menu.on("hide", onHide);
36285            menu.on("beforeshow", onBeforeShow);
36286            menu.on("show", onShow);
36287            var g = menu.group;
36288            if(g && menu.events["checkchange"]){
36289                if(!groups[g]){
36290                    groups[g] = [];
36291                }
36292                groups[g].push(menu);
36293                menu.on("checkchange", onCheck);
36294            }
36295        },
36296
36297         /**
36298          * Returns a {@link Roo.menu.Menu} object
36299          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36300          * be used to generate and return a new Menu instance.
36301          */
36302        get : function(menu){
36303            if(typeof menu == "string"){ // menu id
36304                return menus[menu];
36305            }else if(menu.events){  // menu instance
36306                return menu;
36307            }else if(typeof menu.length == 'number'){ // array of menu items?
36308                return new Roo.menu.Menu({items:menu});
36309            }else{ // otherwise, must be a config
36310                return new Roo.menu.Menu(menu);
36311            }
36312        },
36313
36314        // private
36315        unregister : function(menu){
36316            delete menus[menu.id];
36317            menu.un("beforehide", onBeforeHide);
36318            menu.un("hide", onHide);
36319            menu.un("beforeshow", onBeforeShow);
36320            menu.un("show", onShow);
36321            var g = menu.group;
36322            if(g && menu.events["checkchange"]){
36323                groups[g].remove(menu);
36324                menu.un("checkchange", onCheck);
36325            }
36326        },
36327
36328        // private
36329        registerCheckable : function(menuItem){
36330            var g = menuItem.group;
36331            if(g){
36332                if(!groups[g]){
36333                    groups[g] = [];
36334                }
36335                groups[g].push(menuItem);
36336                menuItem.on("beforecheckchange", onBeforeCheck);
36337            }
36338        },
36339
36340        // private
36341        unregisterCheckable : function(menuItem){
36342            var g = menuItem.group;
36343            if(g){
36344                groups[g].remove(menuItem);
36345                menuItem.un("beforecheckchange", onBeforeCheck);
36346            }
36347        }
36348    };
36349 }();/*
36350  * Based on:
36351  * Ext JS Library 1.1.1
36352  * Copyright(c) 2006-2007, Ext JS, LLC.
36353  *
36354  * Originally Released Under LGPL - original licence link has changed is not relivant.
36355  *
36356  * Fork - LGPL
36357  * <script type="text/javascript">
36358  */
36359  
36360
36361 /**
36362  * @class Roo.menu.BaseItem
36363  * @extends Roo.Component
36364  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36365  * management and base configuration options shared by all menu components.
36366  * @constructor
36367  * Creates a new BaseItem
36368  * @param {Object} config Configuration options
36369  */
36370 Roo.menu.BaseItem = function(config){
36371     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36372
36373     this.addEvents({
36374         /**
36375          * @event click
36376          * Fires when this item is clicked
36377          * @param {Roo.menu.BaseItem} this
36378          * @param {Roo.EventObject} e
36379          */
36380         click: true,
36381         /**
36382          * @event activate
36383          * Fires when this item is activated
36384          * @param {Roo.menu.BaseItem} this
36385          */
36386         activate : true,
36387         /**
36388          * @event deactivate
36389          * Fires when this item is deactivated
36390          * @param {Roo.menu.BaseItem} this
36391          */
36392         deactivate : true
36393     });
36394
36395     if(this.handler){
36396         this.on("click", this.handler, this.scope, true);
36397     }
36398 };
36399
36400 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36401     /**
36402      * @cfg {Function} handler
36403      * A function that will handle the click event of this menu item (defaults to undefined)
36404      */
36405     /**
36406      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36407      */
36408     canActivate : false,
36409     
36410      /**
36411      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36412      */
36413     hidden: false,
36414     
36415     /**
36416      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36417      */
36418     activeClass : "x-menu-item-active",
36419     /**
36420      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36421      */
36422     hideOnClick : true,
36423     /**
36424      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36425      */
36426     hideDelay : 100,
36427
36428     // private
36429     ctype: "Roo.menu.BaseItem",
36430
36431     // private
36432     actionMode : "container",
36433
36434     // private
36435     render : function(container, parentMenu){
36436         this.parentMenu = parentMenu;
36437         Roo.menu.BaseItem.superclass.render.call(this, container);
36438         this.container.menuItemId = this.id;
36439     },
36440
36441     // private
36442     onRender : function(container, position){
36443         this.el = Roo.get(this.el);
36444         container.dom.appendChild(this.el.dom);
36445     },
36446
36447     // private
36448     onClick : function(e){
36449         if(!this.disabled && this.fireEvent("click", this, e) !== false
36450                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36451             this.handleClick(e);
36452         }else{
36453             e.stopEvent();
36454         }
36455     },
36456
36457     // private
36458     activate : function(){
36459         if(this.disabled){
36460             return false;
36461         }
36462         var li = this.container;
36463         li.addClass(this.activeClass);
36464         this.region = li.getRegion().adjust(2, 2, -2, -2);
36465         this.fireEvent("activate", this);
36466         return true;
36467     },
36468
36469     // private
36470     deactivate : function(){
36471         this.container.removeClass(this.activeClass);
36472         this.fireEvent("deactivate", this);
36473     },
36474
36475     // private
36476     shouldDeactivate : function(e){
36477         return !this.region || !this.region.contains(e.getPoint());
36478     },
36479
36480     // private
36481     handleClick : function(e){
36482         if(this.hideOnClick){
36483             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36484         }
36485     },
36486
36487     // private
36488     expandMenu : function(autoActivate){
36489         // do nothing
36490     },
36491
36492     // private
36493     hideMenu : function(){
36494         // do nothing
36495     }
36496 });/*
36497  * Based on:
36498  * Ext JS Library 1.1.1
36499  * Copyright(c) 2006-2007, Ext JS, LLC.
36500  *
36501  * Originally Released Under LGPL - original licence link has changed is not relivant.
36502  *
36503  * Fork - LGPL
36504  * <script type="text/javascript">
36505  */
36506  
36507 /**
36508  * @class Roo.menu.Adapter
36509  * @extends Roo.menu.BaseItem
36510  * 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.
36511  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36512  * @constructor
36513  * Creates a new Adapter
36514  * @param {Object} config Configuration options
36515  */
36516 Roo.menu.Adapter = function(component, config){
36517     Roo.menu.Adapter.superclass.constructor.call(this, config);
36518     this.component = component;
36519 };
36520 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36521     // private
36522     canActivate : true,
36523
36524     // private
36525     onRender : function(container, position){
36526         this.component.render(container);
36527         this.el = this.component.getEl();
36528     },
36529
36530     // private
36531     activate : function(){
36532         if(this.disabled){
36533             return false;
36534         }
36535         this.component.focus();
36536         this.fireEvent("activate", this);
36537         return true;
36538     },
36539
36540     // private
36541     deactivate : function(){
36542         this.fireEvent("deactivate", this);
36543     },
36544
36545     // private
36546     disable : function(){
36547         this.component.disable();
36548         Roo.menu.Adapter.superclass.disable.call(this);
36549     },
36550
36551     // private
36552     enable : function(){
36553         this.component.enable();
36554         Roo.menu.Adapter.superclass.enable.call(this);
36555     }
36556 });/*
36557  * Based on:
36558  * Ext JS Library 1.1.1
36559  * Copyright(c) 2006-2007, Ext JS, LLC.
36560  *
36561  * Originally Released Under LGPL - original licence link has changed is not relivant.
36562  *
36563  * Fork - LGPL
36564  * <script type="text/javascript">
36565  */
36566
36567 /**
36568  * @class Roo.menu.TextItem
36569  * @extends Roo.menu.BaseItem
36570  * Adds a static text string to a menu, usually used as either a heading or group separator.
36571  * Note: old style constructor with text is still supported.
36572  * 
36573  * @constructor
36574  * Creates a new TextItem
36575  * @param {Object} cfg Configuration
36576  */
36577 Roo.menu.TextItem = function(cfg){
36578     if (typeof(cfg) == 'string') {
36579         this.text = cfg;
36580     } else {
36581         Roo.apply(this,cfg);
36582     }
36583     
36584     Roo.menu.TextItem.superclass.constructor.call(this);
36585 };
36586
36587 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36588     /**
36589      * @cfg {Boolean} text Text to show on item.
36590      */
36591     text : '',
36592     
36593     /**
36594      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36595      */
36596     hideOnClick : false,
36597     /**
36598      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36599      */
36600     itemCls : "x-menu-text",
36601
36602     // private
36603     onRender : function(){
36604         var s = document.createElement("span");
36605         s.className = this.itemCls;
36606         s.innerHTML = this.text;
36607         this.el = s;
36608         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36609     }
36610 });/*
36611  * Based on:
36612  * Ext JS Library 1.1.1
36613  * Copyright(c) 2006-2007, Ext JS, LLC.
36614  *
36615  * Originally Released Under LGPL - original licence link has changed is not relivant.
36616  *
36617  * Fork - LGPL
36618  * <script type="text/javascript">
36619  */
36620
36621 /**
36622  * @class Roo.menu.Separator
36623  * @extends Roo.menu.BaseItem
36624  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36625  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36626  * @constructor
36627  * @param {Object} config Configuration options
36628  */
36629 Roo.menu.Separator = function(config){
36630     Roo.menu.Separator.superclass.constructor.call(this, config);
36631 };
36632
36633 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36634     /**
36635      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36636      */
36637     itemCls : "x-menu-sep",
36638     /**
36639      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36640      */
36641     hideOnClick : false,
36642
36643     // private
36644     onRender : function(li){
36645         var s = document.createElement("span");
36646         s.className = this.itemCls;
36647         s.innerHTML = "&#160;";
36648         this.el = s;
36649         li.addClass("x-menu-sep-li");
36650         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36651     }
36652 });/*
36653  * Based on:
36654  * Ext JS Library 1.1.1
36655  * Copyright(c) 2006-2007, Ext JS, LLC.
36656  *
36657  * Originally Released Under LGPL - original licence link has changed is not relivant.
36658  *
36659  * Fork - LGPL
36660  * <script type="text/javascript">
36661  */
36662 /**
36663  * @class Roo.menu.Item
36664  * @extends Roo.menu.BaseItem
36665  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36666  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36667  * activation and click handling.
36668  * @constructor
36669  * Creates a new Item
36670  * @param {Object} config Configuration options
36671  */
36672 Roo.menu.Item = function(config){
36673     Roo.menu.Item.superclass.constructor.call(this, config);
36674     if(this.menu){
36675         this.menu = Roo.menu.MenuMgr.get(this.menu);
36676     }
36677 };
36678 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36679     
36680     /**
36681      * @cfg {String} text
36682      * The text to show on the menu item.
36683      */
36684     text: '',
36685      /**
36686      * @cfg {String} HTML to render in menu
36687      * The text to show on the menu item (HTML version).
36688      */
36689     html: '',
36690     /**
36691      * @cfg {String} icon
36692      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36693      */
36694     icon: undefined,
36695     /**
36696      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36697      */
36698     itemCls : "x-menu-item",
36699     /**
36700      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36701      */
36702     canActivate : true,
36703     /**
36704      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36705      */
36706     showDelay: 200,
36707     // doc'd in BaseItem
36708     hideDelay: 200,
36709
36710     // private
36711     ctype: "Roo.menu.Item",
36712     
36713     // private
36714     onRender : function(container, position){
36715         var el = document.createElement("a");
36716         el.hideFocus = true;
36717         el.unselectable = "on";
36718         el.href = this.href || "#";
36719         if(this.hrefTarget){
36720             el.target = this.hrefTarget;
36721         }
36722         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36723         
36724         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36725         
36726         el.innerHTML = String.format(
36727                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36728                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36729         this.el = el;
36730         Roo.menu.Item.superclass.onRender.call(this, container, position);
36731     },
36732
36733     /**
36734      * Sets the text to display in this menu item
36735      * @param {String} text The text to display
36736      * @param {Boolean} isHTML true to indicate text is pure html.
36737      */
36738     setText : function(text, isHTML){
36739         if (isHTML) {
36740             this.html = text;
36741         } else {
36742             this.text = text;
36743             this.html = '';
36744         }
36745         if(this.rendered){
36746             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36747      
36748             this.el.update(String.format(
36749                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36750                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36751             this.parentMenu.autoWidth();
36752         }
36753     },
36754
36755     // private
36756     handleClick : function(e){
36757         if(!this.href){ // if no link defined, stop the event automatically
36758             e.stopEvent();
36759         }
36760         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36761     },
36762
36763     // private
36764     activate : function(autoExpand){
36765         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36766             this.focus();
36767             if(autoExpand){
36768                 this.expandMenu();
36769             }
36770         }
36771         return true;
36772     },
36773
36774     // private
36775     shouldDeactivate : function(e){
36776         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36777             if(this.menu && this.menu.isVisible()){
36778                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36779             }
36780             return true;
36781         }
36782         return false;
36783     },
36784
36785     // private
36786     deactivate : function(){
36787         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36788         this.hideMenu();
36789     },
36790
36791     // private
36792     expandMenu : function(autoActivate){
36793         if(!this.disabled && this.menu){
36794             clearTimeout(this.hideTimer);
36795             delete this.hideTimer;
36796             if(!this.menu.isVisible() && !this.showTimer){
36797                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36798             }else if (this.menu.isVisible() && autoActivate){
36799                 this.menu.tryActivate(0, 1);
36800             }
36801         }
36802     },
36803
36804     // private
36805     deferExpand : function(autoActivate){
36806         delete this.showTimer;
36807         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36808         if(autoActivate){
36809             this.menu.tryActivate(0, 1);
36810         }
36811     },
36812
36813     // private
36814     hideMenu : function(){
36815         clearTimeout(this.showTimer);
36816         delete this.showTimer;
36817         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36818             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36819         }
36820     },
36821
36822     // private
36823     deferHide : function(){
36824         delete this.hideTimer;
36825         this.menu.hide();
36826     }
36827 });/*
36828  * Based on:
36829  * Ext JS Library 1.1.1
36830  * Copyright(c) 2006-2007, Ext JS, LLC.
36831  *
36832  * Originally Released Under LGPL - original licence link has changed is not relivant.
36833  *
36834  * Fork - LGPL
36835  * <script type="text/javascript">
36836  */
36837  
36838 /**
36839  * @class Roo.menu.CheckItem
36840  * @extends Roo.menu.Item
36841  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36842  * @constructor
36843  * Creates a new CheckItem
36844  * @param {Object} config Configuration options
36845  */
36846 Roo.menu.CheckItem = function(config){
36847     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36848     this.addEvents({
36849         /**
36850          * @event beforecheckchange
36851          * Fires before the checked value is set, providing an opportunity to cancel if needed
36852          * @param {Roo.menu.CheckItem} this
36853          * @param {Boolean} checked The new checked value that will be set
36854          */
36855         "beforecheckchange" : true,
36856         /**
36857          * @event checkchange
36858          * Fires after the checked value has been set
36859          * @param {Roo.menu.CheckItem} this
36860          * @param {Boolean} checked The checked value that was set
36861          */
36862         "checkchange" : true
36863     });
36864     if(this.checkHandler){
36865         this.on('checkchange', this.checkHandler, this.scope);
36866     }
36867 };
36868 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36869     /**
36870      * @cfg {String} group
36871      * All check items with the same group name will automatically be grouped into a single-select
36872      * radio button group (defaults to '')
36873      */
36874     /**
36875      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36876      */
36877     itemCls : "x-menu-item x-menu-check-item",
36878     /**
36879      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36880      */
36881     groupClass : "x-menu-group-item",
36882
36883     /**
36884      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36885      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36886      * initialized with checked = true will be rendered as checked.
36887      */
36888     checked: false,
36889
36890     // private
36891     ctype: "Roo.menu.CheckItem",
36892
36893     // private
36894     onRender : function(c){
36895         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36896         if(this.group){
36897             this.el.addClass(this.groupClass);
36898         }
36899         Roo.menu.MenuMgr.registerCheckable(this);
36900         if(this.checked){
36901             this.checked = false;
36902             this.setChecked(true, true);
36903         }
36904     },
36905
36906     // private
36907     destroy : function(){
36908         if(this.rendered){
36909             Roo.menu.MenuMgr.unregisterCheckable(this);
36910         }
36911         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36912     },
36913
36914     /**
36915      * Set the checked state of this item
36916      * @param {Boolean} checked The new checked value
36917      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36918      */
36919     setChecked : function(state, suppressEvent){
36920         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36921             if(this.container){
36922                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36923             }
36924             this.checked = state;
36925             if(suppressEvent !== true){
36926                 this.fireEvent("checkchange", this, state);
36927             }
36928         }
36929     },
36930
36931     // private
36932     handleClick : function(e){
36933        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36934            this.setChecked(!this.checked);
36935        }
36936        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36937     }
36938 });/*
36939  * Based on:
36940  * Ext JS Library 1.1.1
36941  * Copyright(c) 2006-2007, Ext JS, LLC.
36942  *
36943  * Originally Released Under LGPL - original licence link has changed is not relivant.
36944  *
36945  * Fork - LGPL
36946  * <script type="text/javascript">
36947  */
36948  
36949 /**
36950  * @class Roo.menu.DateItem
36951  * @extends Roo.menu.Adapter
36952  * A menu item that wraps the {@link Roo.DatPicker} component.
36953  * @constructor
36954  * Creates a new DateItem
36955  * @param {Object} config Configuration options
36956  */
36957 Roo.menu.DateItem = function(config){
36958     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36959     /** The Roo.DatePicker object @type Roo.DatePicker */
36960     this.picker = this.component;
36961     this.addEvents({select: true});
36962     
36963     this.picker.on("render", function(picker){
36964         picker.getEl().swallowEvent("click");
36965         picker.container.addClass("x-menu-date-item");
36966     });
36967
36968     this.picker.on("select", this.onSelect, this);
36969 };
36970
36971 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36972     // private
36973     onSelect : function(picker, date){
36974         this.fireEvent("select", this, date, picker);
36975         Roo.menu.DateItem.superclass.handleClick.call(this);
36976     }
36977 });/*
36978  * Based on:
36979  * Ext JS Library 1.1.1
36980  * Copyright(c) 2006-2007, Ext JS, LLC.
36981  *
36982  * Originally Released Under LGPL - original licence link has changed is not relivant.
36983  *
36984  * Fork - LGPL
36985  * <script type="text/javascript">
36986  */
36987  
36988 /**
36989  * @class Roo.menu.ColorItem
36990  * @extends Roo.menu.Adapter
36991  * A menu item that wraps the {@link Roo.ColorPalette} component.
36992  * @constructor
36993  * Creates a new ColorItem
36994  * @param {Object} config Configuration options
36995  */
36996 Roo.menu.ColorItem = function(config){
36997     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36998     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36999     this.palette = this.component;
37000     this.relayEvents(this.palette, ["select"]);
37001     if(this.selectHandler){
37002         this.on('select', this.selectHandler, this.scope);
37003     }
37004 };
37005 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
37006  * Based on:
37007  * Ext JS Library 1.1.1
37008  * Copyright(c) 2006-2007, Ext JS, LLC.
37009  *
37010  * Originally Released Under LGPL - original licence link has changed is not relivant.
37011  *
37012  * Fork - LGPL
37013  * <script type="text/javascript">
37014  */
37015  
37016
37017 /**
37018  * @class Roo.menu.DateMenu
37019  * @extends Roo.menu.Menu
37020  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
37021  * @constructor
37022  * Creates a new DateMenu
37023  * @param {Object} config Configuration options
37024  */
37025 Roo.menu.DateMenu = function(config){
37026     Roo.menu.DateMenu.superclass.constructor.call(this, config);
37027     this.plain = true;
37028     var di = new Roo.menu.DateItem(config);
37029     this.add(di);
37030     /**
37031      * The {@link Roo.DatePicker} instance for this DateMenu
37032      * @type DatePicker
37033      */
37034     this.picker = di.picker;
37035     /**
37036      * @event select
37037      * @param {DatePicker} picker
37038      * @param {Date} date
37039      */
37040     this.relayEvents(di, ["select"]);
37041     this.on('beforeshow', function(){
37042         if(this.picker){
37043             this.picker.hideMonthPicker(false);
37044         }
37045     }, this);
37046 };
37047 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37048     cls:'x-date-menu'
37049 });/*
37050  * Based on:
37051  * Ext JS Library 1.1.1
37052  * Copyright(c) 2006-2007, Ext JS, LLC.
37053  *
37054  * Originally Released Under LGPL - original licence link has changed is not relivant.
37055  *
37056  * Fork - LGPL
37057  * <script type="text/javascript">
37058  */
37059  
37060
37061 /**
37062  * @class Roo.menu.ColorMenu
37063  * @extends Roo.menu.Menu
37064  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37065  * @constructor
37066  * Creates a new ColorMenu
37067  * @param {Object} config Configuration options
37068  */
37069 Roo.menu.ColorMenu = function(config){
37070     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37071     this.plain = true;
37072     var ci = new Roo.menu.ColorItem(config);
37073     this.add(ci);
37074     /**
37075      * The {@link Roo.ColorPalette} instance for this ColorMenu
37076      * @type ColorPalette
37077      */
37078     this.palette = ci.palette;
37079     /**
37080      * @event select
37081      * @param {ColorPalette} palette
37082      * @param {String} color
37083      */
37084     this.relayEvents(ci, ["select"]);
37085 };
37086 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37087  * Based on:
37088  * Ext JS Library 1.1.1
37089  * Copyright(c) 2006-2007, Ext JS, LLC.
37090  *
37091  * Originally Released Under LGPL - original licence link has changed is not relivant.
37092  *
37093  * Fork - LGPL
37094  * <script type="text/javascript">
37095  */
37096  
37097 /**
37098  * @class Roo.form.Field
37099  * @extends Roo.BoxComponent
37100  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37101  * @constructor
37102  * Creates a new Field
37103  * @param {Object} config Configuration options
37104  */
37105 Roo.form.Field = function(config){
37106     Roo.form.Field.superclass.constructor.call(this, config);
37107 };
37108
37109 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37110     /**
37111      * @cfg {String} fieldLabel Label to use when rendering a form.
37112      */
37113        /**
37114      * @cfg {String} qtip Mouse over tip
37115      */
37116      
37117     /**
37118      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37119      */
37120     invalidClass : "x-form-invalid",
37121     /**
37122      * @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")
37123      */
37124     invalidText : "The value in this field is invalid",
37125     /**
37126      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37127      */
37128     focusClass : "x-form-focus",
37129     /**
37130      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37131       automatic validation (defaults to "keyup").
37132      */
37133     validationEvent : "keyup",
37134     /**
37135      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37136      */
37137     validateOnBlur : true,
37138     /**
37139      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37140      */
37141     validationDelay : 250,
37142     /**
37143      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37144      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37145      */
37146     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37147     /**
37148      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37149      */
37150     fieldClass : "x-form-field",
37151     /**
37152      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37153      *<pre>
37154 Value         Description
37155 -----------   ----------------------------------------------------------------------
37156 qtip          Display a quick tip when the user hovers over the field
37157 title         Display a default browser title attribute popup
37158 under         Add a block div beneath the field containing the error text
37159 side          Add an error icon to the right of the field with a popup on hover
37160 [element id]  Add the error text directly to the innerHTML of the specified element
37161 </pre>
37162      */
37163     msgTarget : 'qtip',
37164     /**
37165      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37166      */
37167     msgFx : 'normal',
37168
37169     /**
37170      * @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.
37171      */
37172     readOnly : false,
37173
37174     /**
37175      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37176      */
37177     disabled : false,
37178
37179     /**
37180      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37181      */
37182     inputType : undefined,
37183     
37184     /**
37185      * @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).
37186          */
37187         tabIndex : undefined,
37188         
37189     // private
37190     isFormField : true,
37191
37192     // private
37193     hasFocus : false,
37194     /**
37195      * @property {Roo.Element} fieldEl
37196      * Element Containing the rendered Field (with label etc.)
37197      */
37198     /**
37199      * @cfg {Mixed} value A value to initialize this field with.
37200      */
37201     value : undefined,
37202
37203     /**
37204      * @cfg {String} name The field's HTML name attribute.
37205      */
37206     /**
37207      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37208      */
37209     // private
37210     loadedValue : false,
37211      
37212      
37213         // private ??
37214         initComponent : function(){
37215         Roo.form.Field.superclass.initComponent.call(this);
37216         this.addEvents({
37217             /**
37218              * @event focus
37219              * Fires when this field receives input focus.
37220              * @param {Roo.form.Field} this
37221              */
37222             focus : true,
37223             /**
37224              * @event blur
37225              * Fires when this field loses input focus.
37226              * @param {Roo.form.Field} this
37227              */
37228             blur : true,
37229             /**
37230              * @event specialkey
37231              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37232              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37233              * @param {Roo.form.Field} this
37234              * @param {Roo.EventObject} e The event object
37235              */
37236             specialkey : true,
37237             /**
37238              * @event change
37239              * Fires just before the field blurs if the field value has changed.
37240              * @param {Roo.form.Field} this
37241              * @param {Mixed} newValue The new value
37242              * @param {Mixed} oldValue The original value
37243              */
37244             change : true,
37245             /**
37246              * @event invalid
37247              * Fires after the field has been marked as invalid.
37248              * @param {Roo.form.Field} this
37249              * @param {String} msg The validation message
37250              */
37251             invalid : true,
37252             /**
37253              * @event valid
37254              * Fires after the field has been validated with no errors.
37255              * @param {Roo.form.Field} this
37256              */
37257             valid : true,
37258              /**
37259              * @event keyup
37260              * Fires after the key up
37261              * @param {Roo.form.Field} this
37262              * @param {Roo.EventObject}  e The event Object
37263              */
37264             keyup : true
37265         });
37266     },
37267
37268     /**
37269      * Returns the name attribute of the field if available
37270      * @return {String} name The field name
37271      */
37272     getName: function(){
37273          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37274     },
37275
37276     // private
37277     onRender : function(ct, position){
37278         Roo.form.Field.superclass.onRender.call(this, ct, position);
37279         if(!this.el){
37280             var cfg = this.getAutoCreate();
37281             if(!cfg.name){
37282                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37283             }
37284             if (!cfg.name.length) {
37285                 delete cfg.name;
37286             }
37287             if(this.inputType){
37288                 cfg.type = this.inputType;
37289             }
37290             this.el = ct.createChild(cfg, position);
37291         }
37292         var type = this.el.dom.type;
37293         if(type){
37294             if(type == 'password'){
37295                 type = 'text';
37296             }
37297             this.el.addClass('x-form-'+type);
37298         }
37299         if(this.readOnly){
37300             this.el.dom.readOnly = true;
37301         }
37302         if(this.tabIndex !== undefined){
37303             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37304         }
37305
37306         this.el.addClass([this.fieldClass, this.cls]);
37307         this.initValue();
37308     },
37309
37310     /**
37311      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37312      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37313      * @return {Roo.form.Field} this
37314      */
37315     applyTo : function(target){
37316         this.allowDomMove = false;
37317         this.el = Roo.get(target);
37318         this.render(this.el.dom.parentNode);
37319         return this;
37320     },
37321
37322     // private
37323     initValue : function(){
37324         if(this.value !== undefined){
37325             this.setValue(this.value);
37326         }else if(this.el.dom.value.length > 0){
37327             this.setValue(this.el.dom.value);
37328         }
37329     },
37330
37331     /**
37332      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37333      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
37334      */
37335     isDirty : function() {
37336         if(this.disabled) {
37337             return false;
37338         }
37339         return String(this.getValue()) !== String(this.originalValue);
37340     },
37341
37342     /**
37343      * stores the current value in loadedValue
37344      */
37345     resetHasChanged : function()
37346     {
37347         this.loadedValue = String(this.getValue());
37348     },
37349     /**
37350      * checks the current value against the 'loaded' value.
37351      * Note - will return false if 'resetHasChanged' has not been called first.
37352      */
37353     hasChanged : function()
37354     {
37355         if(this.disabled || this.readOnly) {
37356             return false;
37357         }
37358         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
37359     },
37360     
37361     
37362     
37363     // private
37364     afterRender : function(){
37365         Roo.form.Field.superclass.afterRender.call(this);
37366         this.initEvents();
37367     },
37368
37369     // private
37370     fireKey : function(e){
37371         //Roo.log('field ' + e.getKey());
37372         if(e.isNavKeyPress()){
37373             this.fireEvent("specialkey", this, e);
37374         }
37375     },
37376
37377     /**
37378      * Resets the current field value to the originally loaded value and clears any validation messages
37379      */
37380     reset : function(){
37381         this.setValue(this.resetValue);
37382         this.clearInvalid();
37383     },
37384
37385     // private
37386     initEvents : function(){
37387         // safari killled keypress - so keydown is now used..
37388         this.el.on("keydown" , this.fireKey,  this);
37389         this.el.on("focus", this.onFocus,  this);
37390         this.el.on("blur", this.onBlur,  this);
37391         this.el.relayEvent('keyup', this);
37392
37393         // reference to original value for reset
37394         this.originalValue = this.getValue();
37395         this.resetValue =  this.getValue();
37396     },
37397
37398     // private
37399     onFocus : function(){
37400         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37401             this.el.addClass(this.focusClass);
37402         }
37403         if(!this.hasFocus){
37404             this.hasFocus = true;
37405             this.startValue = this.getValue();
37406             this.fireEvent("focus", this);
37407         }
37408     },
37409
37410     beforeBlur : Roo.emptyFn,
37411
37412     // private
37413     onBlur : function(){
37414         this.beforeBlur();
37415         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37416             this.el.removeClass(this.focusClass);
37417         }
37418         this.hasFocus = false;
37419         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37420             this.validate();
37421         }
37422         var v = this.getValue();
37423         if(String(v) !== String(this.startValue)){
37424             this.fireEvent('change', this, v, this.startValue);
37425         }
37426         this.fireEvent("blur", this);
37427     },
37428
37429     /**
37430      * Returns whether or not the field value is currently valid
37431      * @param {Boolean} preventMark True to disable marking the field invalid
37432      * @return {Boolean} True if the value is valid, else false
37433      */
37434     isValid : function(preventMark){
37435         if(this.disabled){
37436             return true;
37437         }
37438         var restore = this.preventMark;
37439         this.preventMark = preventMark === true;
37440         var v = this.validateValue(this.processValue(this.getRawValue()));
37441         this.preventMark = restore;
37442         return v;
37443     },
37444
37445     /**
37446      * Validates the field value
37447      * @return {Boolean} True if the value is valid, else false
37448      */
37449     validate : function(){
37450         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37451             this.clearInvalid();
37452             return true;
37453         }
37454         return false;
37455     },
37456
37457     processValue : function(value){
37458         return value;
37459     },
37460
37461     // private
37462     // Subclasses should provide the validation implementation by overriding this
37463     validateValue : function(value){
37464         return true;
37465     },
37466
37467     /**
37468      * Mark this field as invalid
37469      * @param {String} msg The validation message
37470      */
37471     markInvalid : function(msg){
37472         if(!this.rendered || this.preventMark){ // not rendered
37473             return;
37474         }
37475         
37476         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37477         
37478         obj.el.addClass(this.invalidClass);
37479         msg = msg || this.invalidText;
37480         switch(this.msgTarget){
37481             case 'qtip':
37482                 obj.el.dom.qtip = msg;
37483                 obj.el.dom.qclass = 'x-form-invalid-tip';
37484                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37485                     Roo.QuickTips.enable();
37486                 }
37487                 break;
37488             case 'title':
37489                 this.el.dom.title = msg;
37490                 break;
37491             case 'under':
37492                 if(!this.errorEl){
37493                     var elp = this.el.findParent('.x-form-element', 5, true);
37494                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37495                     this.errorEl.setWidth(elp.getWidth(true)-20);
37496                 }
37497                 this.errorEl.update(msg);
37498                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37499                 break;
37500             case 'side':
37501                 if(!this.errorIcon){
37502                     var elp = this.el.findParent('.x-form-element', 5, true);
37503                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37504                 }
37505                 this.alignErrorIcon();
37506                 this.errorIcon.dom.qtip = msg;
37507                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37508                 this.errorIcon.show();
37509                 this.on('resize', this.alignErrorIcon, this);
37510                 break;
37511             default:
37512                 var t = Roo.getDom(this.msgTarget);
37513                 t.innerHTML = msg;
37514                 t.style.display = this.msgDisplay;
37515                 break;
37516         }
37517         this.fireEvent('invalid', this, msg);
37518     },
37519
37520     // private
37521     alignErrorIcon : function(){
37522         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37523     },
37524
37525     /**
37526      * Clear any invalid styles/messages for this field
37527      */
37528     clearInvalid : function(){
37529         if(!this.rendered || this.preventMark){ // not rendered
37530             return;
37531         }
37532         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37533         
37534         obj.el.removeClass(this.invalidClass);
37535         switch(this.msgTarget){
37536             case 'qtip':
37537                 obj.el.dom.qtip = '';
37538                 break;
37539             case 'title':
37540                 this.el.dom.title = '';
37541                 break;
37542             case 'under':
37543                 if(this.errorEl){
37544                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37545                 }
37546                 break;
37547             case 'side':
37548                 if(this.errorIcon){
37549                     this.errorIcon.dom.qtip = '';
37550                     this.errorIcon.hide();
37551                     this.un('resize', this.alignErrorIcon, this);
37552                 }
37553                 break;
37554             default:
37555                 var t = Roo.getDom(this.msgTarget);
37556                 t.innerHTML = '';
37557                 t.style.display = 'none';
37558                 break;
37559         }
37560         this.fireEvent('valid', this);
37561     },
37562
37563     /**
37564      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37565      * @return {Mixed} value The field value
37566      */
37567     getRawValue : function(){
37568         var v = this.el.getValue();
37569         
37570         return v;
37571     },
37572
37573     /**
37574      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37575      * @return {Mixed} value The field value
37576      */
37577     getValue : function(){
37578         var v = this.el.getValue();
37579          
37580         return v;
37581     },
37582
37583     /**
37584      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37585      * @param {Mixed} value The value to set
37586      */
37587     setRawValue : function(v){
37588         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37589     },
37590
37591     /**
37592      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37593      * @param {Mixed} value The value to set
37594      */
37595     setValue : function(v){
37596         this.value = v;
37597         if(this.rendered){
37598             this.el.dom.value = (v === null || v === undefined ? '' : v);
37599              this.validate();
37600         }
37601     },
37602
37603     adjustSize : function(w, h){
37604         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37605         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37606         return s;
37607     },
37608
37609     adjustWidth : function(tag, w){
37610         tag = tag.toLowerCase();
37611         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37612             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37613                 if(tag == 'input'){
37614                     return w + 2;
37615                 }
37616                 if(tag == 'textarea'){
37617                     return w-2;
37618                 }
37619             }else if(Roo.isOpera){
37620                 if(tag == 'input'){
37621                     return w + 2;
37622                 }
37623                 if(tag == 'textarea'){
37624                     return w-2;
37625                 }
37626             }
37627         }
37628         return w;
37629     }
37630 });
37631
37632
37633 // anything other than normal should be considered experimental
37634 Roo.form.Field.msgFx = {
37635     normal : {
37636         show: function(msgEl, f){
37637             msgEl.setDisplayed('block');
37638         },
37639
37640         hide : function(msgEl, f){
37641             msgEl.setDisplayed(false).update('');
37642         }
37643     },
37644
37645     slide : {
37646         show: function(msgEl, f){
37647             msgEl.slideIn('t', {stopFx:true});
37648         },
37649
37650         hide : function(msgEl, f){
37651             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37652         }
37653     },
37654
37655     slideRight : {
37656         show: function(msgEl, f){
37657             msgEl.fixDisplay();
37658             msgEl.alignTo(f.el, 'tl-tr');
37659             msgEl.slideIn('l', {stopFx:true});
37660         },
37661
37662         hide : function(msgEl, f){
37663             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37664         }
37665     }
37666 };/*
37667  * Based on:
37668  * Ext JS Library 1.1.1
37669  * Copyright(c) 2006-2007, Ext JS, LLC.
37670  *
37671  * Originally Released Under LGPL - original licence link has changed is not relivant.
37672  *
37673  * Fork - LGPL
37674  * <script type="text/javascript">
37675  */
37676  
37677
37678 /**
37679  * @class Roo.form.TextField
37680  * @extends Roo.form.Field
37681  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37682  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37683  * @constructor
37684  * Creates a new TextField
37685  * @param {Object} config Configuration options
37686  */
37687 Roo.form.TextField = function(config){
37688     Roo.form.TextField.superclass.constructor.call(this, config);
37689     this.addEvents({
37690         /**
37691          * @event autosize
37692          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37693          * according to the default logic, but this event provides a hook for the developer to apply additional
37694          * logic at runtime to resize the field if needed.
37695              * @param {Roo.form.Field} this This text field
37696              * @param {Number} width The new field width
37697              */
37698         autosize : true
37699     });
37700 };
37701
37702 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37703     /**
37704      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37705      */
37706     grow : false,
37707     /**
37708      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37709      */
37710     growMin : 30,
37711     /**
37712      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37713      */
37714     growMax : 800,
37715     /**
37716      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37717      */
37718     vtype : null,
37719     /**
37720      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37721      */
37722     maskRe : null,
37723     /**
37724      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37725      */
37726     disableKeyFilter : false,
37727     /**
37728      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37729      */
37730     allowBlank : true,
37731     /**
37732      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37733      */
37734     minLength : 0,
37735     /**
37736      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37737      */
37738     maxLength : Number.MAX_VALUE,
37739     /**
37740      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37741      */
37742     minLengthText : "The minimum length for this field is {0}",
37743     /**
37744      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37745      */
37746     maxLengthText : "The maximum length for this field is {0}",
37747     /**
37748      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37749      */
37750     selectOnFocus : false,
37751     /**
37752      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37753      */
37754     blankText : "This field is required",
37755     /**
37756      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37757      * If available, this function will be called only after the basic validators all return true, and will be passed the
37758      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37759      */
37760     validator : null,
37761     /**
37762      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37763      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37764      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37765      */
37766     regex : null,
37767     /**
37768      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37769      */
37770     regexText : "",
37771     /**
37772      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37773      */
37774     emptyText : null,
37775    
37776
37777     // private
37778     initEvents : function()
37779     {
37780         if (this.emptyText) {
37781             this.el.attr('placeholder', this.emptyText);
37782         }
37783         
37784         Roo.form.TextField.superclass.initEvents.call(this);
37785         if(this.validationEvent == 'keyup'){
37786             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37787             this.el.on('keyup', this.filterValidation, this);
37788         }
37789         else if(this.validationEvent !== false){
37790             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37791         }
37792         
37793         if(this.selectOnFocus){
37794             this.on("focus", this.preFocus, this);
37795             
37796         }
37797         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37798             this.el.on("keypress", this.filterKeys, this);
37799         }
37800         if(this.grow){
37801             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37802             this.el.on("click", this.autoSize,  this);
37803         }
37804         if(this.el.is('input[type=password]') && Roo.isSafari){
37805             this.el.on('keydown', this.SafariOnKeyDown, this);
37806         }
37807     },
37808
37809     processValue : function(value){
37810         if(this.stripCharsRe){
37811             var newValue = value.replace(this.stripCharsRe, '');
37812             if(newValue !== value){
37813                 this.setRawValue(newValue);
37814                 return newValue;
37815             }
37816         }
37817         return value;
37818     },
37819
37820     filterValidation : function(e){
37821         if(!e.isNavKeyPress()){
37822             this.validationTask.delay(this.validationDelay);
37823         }
37824     },
37825
37826     // private
37827     onKeyUp : function(e){
37828         if(!e.isNavKeyPress()){
37829             this.autoSize();
37830         }
37831     },
37832
37833     /**
37834      * Resets the current field value to the originally-loaded value and clears any validation messages.
37835      *  
37836      */
37837     reset : function(){
37838         Roo.form.TextField.superclass.reset.call(this);
37839        
37840     },
37841
37842     
37843     // private
37844     preFocus : function(){
37845         
37846         if(this.selectOnFocus){
37847             this.el.dom.select();
37848         }
37849     },
37850
37851     
37852     // private
37853     filterKeys : function(e){
37854         var k = e.getKey();
37855         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37856             return;
37857         }
37858         var c = e.getCharCode(), cc = String.fromCharCode(c);
37859         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37860             return;
37861         }
37862         if(!this.maskRe.test(cc)){
37863             e.stopEvent();
37864         }
37865     },
37866
37867     setValue : function(v){
37868         
37869         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37870         
37871         this.autoSize();
37872     },
37873
37874     /**
37875      * Validates a value according to the field's validation rules and marks the field as invalid
37876      * if the validation fails
37877      * @param {Mixed} value The value to validate
37878      * @return {Boolean} True if the value is valid, else false
37879      */
37880     validateValue : function(value){
37881         if(value.length < 1)  { // if it's blank
37882              if(this.allowBlank){
37883                 this.clearInvalid();
37884                 return true;
37885              }else{
37886                 this.markInvalid(this.blankText);
37887                 return false;
37888              }
37889         }
37890         if(value.length < this.minLength){
37891             this.markInvalid(String.format(this.minLengthText, this.minLength));
37892             return false;
37893         }
37894         if(value.length > this.maxLength){
37895             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37896             return false;
37897         }
37898         if(this.vtype){
37899             var vt = Roo.form.VTypes;
37900             if(!vt[this.vtype](value, this)){
37901                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37902                 return false;
37903             }
37904         }
37905         if(typeof this.validator == "function"){
37906             var msg = this.validator(value);
37907             if(msg !== true){
37908                 this.markInvalid(msg);
37909                 return false;
37910             }
37911         }
37912         if(this.regex && !this.regex.test(value)){
37913             this.markInvalid(this.regexText);
37914             return false;
37915         }
37916         return true;
37917     },
37918
37919     /**
37920      * Selects text in this field
37921      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37922      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37923      */
37924     selectText : function(start, end){
37925         var v = this.getRawValue();
37926         if(v.length > 0){
37927             start = start === undefined ? 0 : start;
37928             end = end === undefined ? v.length : end;
37929             var d = this.el.dom;
37930             if(d.setSelectionRange){
37931                 d.setSelectionRange(start, end);
37932             }else if(d.createTextRange){
37933                 var range = d.createTextRange();
37934                 range.moveStart("character", start);
37935                 range.moveEnd("character", v.length-end);
37936                 range.select();
37937             }
37938         }
37939     },
37940
37941     /**
37942      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37943      * This only takes effect if grow = true, and fires the autosize event.
37944      */
37945     autoSize : function(){
37946         if(!this.grow || !this.rendered){
37947             return;
37948         }
37949         if(!this.metrics){
37950             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37951         }
37952         var el = this.el;
37953         var v = el.dom.value;
37954         var d = document.createElement('div');
37955         d.appendChild(document.createTextNode(v));
37956         v = d.innerHTML;
37957         d = null;
37958         v += "&#160;";
37959         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37960         this.el.setWidth(w);
37961         this.fireEvent("autosize", this, w);
37962     },
37963     
37964     // private
37965     SafariOnKeyDown : function(event)
37966     {
37967         // this is a workaround for a password hang bug on chrome/ webkit.
37968         
37969         var isSelectAll = false;
37970         
37971         if(this.el.dom.selectionEnd > 0){
37972             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37973         }
37974         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37975             event.preventDefault();
37976             this.setValue('');
37977             return;
37978         }
37979         
37980         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37981             
37982             event.preventDefault();
37983             // this is very hacky as keydown always get's upper case.
37984             
37985             var cc = String.fromCharCode(event.getCharCode());
37986             
37987             
37988             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37989             
37990         }
37991         
37992         
37993     }
37994 });/*
37995  * Based on:
37996  * Ext JS Library 1.1.1
37997  * Copyright(c) 2006-2007, Ext JS, LLC.
37998  *
37999  * Originally Released Under LGPL - original licence link has changed is not relivant.
38000  *
38001  * Fork - LGPL
38002  * <script type="text/javascript">
38003  */
38004  
38005 /**
38006  * @class Roo.form.Hidden
38007  * @extends Roo.form.TextField
38008  * Simple Hidden element used on forms 
38009  * 
38010  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
38011  * 
38012  * @constructor
38013  * Creates a new Hidden form element.
38014  * @param {Object} config Configuration options
38015  */
38016
38017
38018
38019 // easy hidden field...
38020 Roo.form.Hidden = function(config){
38021     Roo.form.Hidden.superclass.constructor.call(this, config);
38022 };
38023   
38024 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
38025     fieldLabel:      '',
38026     inputType:      'hidden',
38027     width:          50,
38028     allowBlank:     true,
38029     labelSeparator: '',
38030     hidden:         true,
38031     itemCls :       'x-form-item-display-none'
38032
38033
38034 });
38035
38036
38037 /*
38038  * Based on:
38039  * Ext JS Library 1.1.1
38040  * Copyright(c) 2006-2007, Ext JS, LLC.
38041  *
38042  * Originally Released Under LGPL - original licence link has changed is not relivant.
38043  *
38044  * Fork - LGPL
38045  * <script type="text/javascript">
38046  */
38047  
38048 /**
38049  * @class Roo.form.TriggerField
38050  * @extends Roo.form.TextField
38051  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
38052  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38053  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38054  * for which you can provide a custom implementation.  For example:
38055  * <pre><code>
38056 var trigger = new Roo.form.TriggerField();
38057 trigger.onTriggerClick = myTriggerFn;
38058 trigger.applyTo('my-field');
38059 </code></pre>
38060  *
38061  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38062  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38063  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38064  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38065  * @constructor
38066  * Create a new TriggerField.
38067  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38068  * to the base TextField)
38069  */
38070 Roo.form.TriggerField = function(config){
38071     this.mimicing = false;
38072     Roo.form.TriggerField.superclass.constructor.call(this, config);
38073 };
38074
38075 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38076     /**
38077      * @cfg {String} triggerClass A CSS class to apply to the trigger
38078      */
38079     /**
38080      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38081      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38082      */
38083     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38084     /**
38085      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38086      */
38087     hideTrigger:false,
38088
38089     /** @cfg {Boolean} grow @hide */
38090     /** @cfg {Number} growMin @hide */
38091     /** @cfg {Number} growMax @hide */
38092
38093     /**
38094      * @hide 
38095      * @method
38096      */
38097     autoSize: Roo.emptyFn,
38098     // private
38099     monitorTab : true,
38100     // private
38101     deferHeight : true,
38102
38103     
38104     actionMode : 'wrap',
38105     // private
38106     onResize : function(w, h){
38107         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38108         if(typeof w == 'number'){
38109             var x = w - this.trigger.getWidth();
38110             this.el.setWidth(this.adjustWidth('input', x));
38111             this.trigger.setStyle('left', x+'px');
38112         }
38113     },
38114
38115     // private
38116     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38117
38118     // private
38119     getResizeEl : function(){
38120         return this.wrap;
38121     },
38122
38123     // private
38124     getPositionEl : function(){
38125         return this.wrap;
38126     },
38127
38128     // private
38129     alignErrorIcon : function(){
38130         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38131     },
38132
38133     // private
38134     onRender : function(ct, position){
38135         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38136         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38137         this.trigger = this.wrap.createChild(this.triggerConfig ||
38138                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38139         if(this.hideTrigger){
38140             this.trigger.setDisplayed(false);
38141         }
38142         this.initTrigger();
38143         if(!this.width){
38144             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38145         }
38146     },
38147
38148     // private
38149     initTrigger : function(){
38150         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38151         this.trigger.addClassOnOver('x-form-trigger-over');
38152         this.trigger.addClassOnClick('x-form-trigger-click');
38153     },
38154
38155     // private
38156     onDestroy : function(){
38157         if(this.trigger){
38158             this.trigger.removeAllListeners();
38159             this.trigger.remove();
38160         }
38161         if(this.wrap){
38162             this.wrap.remove();
38163         }
38164         Roo.form.TriggerField.superclass.onDestroy.call(this);
38165     },
38166
38167     // private
38168     onFocus : function(){
38169         Roo.form.TriggerField.superclass.onFocus.call(this);
38170         if(!this.mimicing){
38171             this.wrap.addClass('x-trigger-wrap-focus');
38172             this.mimicing = true;
38173             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38174             if(this.monitorTab){
38175                 this.el.on("keydown", this.checkTab, this);
38176             }
38177         }
38178     },
38179
38180     // private
38181     checkTab : function(e){
38182         if(e.getKey() == e.TAB){
38183             this.triggerBlur();
38184         }
38185     },
38186
38187     // private
38188     onBlur : function(){
38189         // do nothing
38190     },
38191
38192     // private
38193     mimicBlur : function(e, t){
38194         if(!this.wrap.contains(t) && this.validateBlur()){
38195             this.triggerBlur();
38196         }
38197     },
38198
38199     // private
38200     triggerBlur : function(){
38201         this.mimicing = false;
38202         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38203         if(this.monitorTab){
38204             this.el.un("keydown", this.checkTab, this);
38205         }
38206         this.wrap.removeClass('x-trigger-wrap-focus');
38207         Roo.form.TriggerField.superclass.onBlur.call(this);
38208     },
38209
38210     // private
38211     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38212     validateBlur : function(e, t){
38213         return true;
38214     },
38215
38216     // private
38217     onDisable : function(){
38218         Roo.form.TriggerField.superclass.onDisable.call(this);
38219         if(this.wrap){
38220             this.wrap.addClass('x-item-disabled');
38221         }
38222     },
38223
38224     // private
38225     onEnable : function(){
38226         Roo.form.TriggerField.superclass.onEnable.call(this);
38227         if(this.wrap){
38228             this.wrap.removeClass('x-item-disabled');
38229         }
38230     },
38231
38232     // private
38233     onShow : function(){
38234         var ae = this.getActionEl();
38235         
38236         if(ae){
38237             ae.dom.style.display = '';
38238             ae.dom.style.visibility = 'visible';
38239         }
38240     },
38241
38242     // private
38243     
38244     onHide : function(){
38245         var ae = this.getActionEl();
38246         ae.dom.style.display = 'none';
38247     },
38248
38249     /**
38250      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38251      * by an implementing function.
38252      * @method
38253      * @param {EventObject} e
38254      */
38255     onTriggerClick : Roo.emptyFn
38256 });
38257
38258 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38259 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38260 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38261 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38262     initComponent : function(){
38263         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38264
38265         this.triggerConfig = {
38266             tag:'span', cls:'x-form-twin-triggers', cn:[
38267             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38268             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38269         ]};
38270     },
38271
38272     getTrigger : function(index){
38273         return this.triggers[index];
38274     },
38275
38276     initTrigger : function(){
38277         var ts = this.trigger.select('.x-form-trigger', true);
38278         this.wrap.setStyle('overflow', 'hidden');
38279         var triggerField = this;
38280         ts.each(function(t, all, index){
38281             t.hide = function(){
38282                 var w = triggerField.wrap.getWidth();
38283                 this.dom.style.display = 'none';
38284                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38285             };
38286             t.show = function(){
38287                 var w = triggerField.wrap.getWidth();
38288                 this.dom.style.display = '';
38289                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38290             };
38291             var triggerIndex = 'Trigger'+(index+1);
38292
38293             if(this['hide'+triggerIndex]){
38294                 t.dom.style.display = 'none';
38295             }
38296             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38297             t.addClassOnOver('x-form-trigger-over');
38298             t.addClassOnClick('x-form-trigger-click');
38299         }, this);
38300         this.triggers = ts.elements;
38301     },
38302
38303     onTrigger1Click : Roo.emptyFn,
38304     onTrigger2Click : Roo.emptyFn
38305 });/*
38306  * Based on:
38307  * Ext JS Library 1.1.1
38308  * Copyright(c) 2006-2007, Ext JS, LLC.
38309  *
38310  * Originally Released Under LGPL - original licence link has changed is not relivant.
38311  *
38312  * Fork - LGPL
38313  * <script type="text/javascript">
38314  */
38315  
38316 /**
38317  * @class Roo.form.TextArea
38318  * @extends Roo.form.TextField
38319  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38320  * support for auto-sizing.
38321  * @constructor
38322  * Creates a new TextArea
38323  * @param {Object} config Configuration options
38324  */
38325 Roo.form.TextArea = function(config){
38326     Roo.form.TextArea.superclass.constructor.call(this, config);
38327     // these are provided exchanges for backwards compat
38328     // minHeight/maxHeight were replaced by growMin/growMax to be
38329     // compatible with TextField growing config values
38330     if(this.minHeight !== undefined){
38331         this.growMin = this.minHeight;
38332     }
38333     if(this.maxHeight !== undefined){
38334         this.growMax = this.maxHeight;
38335     }
38336 };
38337
38338 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38339     /**
38340      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38341      */
38342     growMin : 60,
38343     /**
38344      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38345      */
38346     growMax: 1000,
38347     /**
38348      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38349      * in the field (equivalent to setting overflow: hidden, defaults to false)
38350      */
38351     preventScrollbars: false,
38352     /**
38353      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38354      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38355      */
38356
38357     // private
38358     onRender : function(ct, position){
38359         if(!this.el){
38360             this.defaultAutoCreate = {
38361                 tag: "textarea",
38362                 style:"width:300px;height:60px;",
38363                 autocomplete: "new-password"
38364             };
38365         }
38366         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38367         if(this.grow){
38368             this.textSizeEl = Roo.DomHelper.append(document.body, {
38369                 tag: "pre", cls: "x-form-grow-sizer"
38370             });
38371             if(this.preventScrollbars){
38372                 this.el.setStyle("overflow", "hidden");
38373             }
38374             this.el.setHeight(this.growMin);
38375         }
38376     },
38377
38378     onDestroy : function(){
38379         if(this.textSizeEl){
38380             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38381         }
38382         Roo.form.TextArea.superclass.onDestroy.call(this);
38383     },
38384
38385     // private
38386     onKeyUp : function(e){
38387         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38388             this.autoSize();
38389         }
38390     },
38391
38392     /**
38393      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38394      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38395      */
38396     autoSize : function(){
38397         if(!this.grow || !this.textSizeEl){
38398             return;
38399         }
38400         var el = this.el;
38401         var v = el.dom.value;
38402         var ts = this.textSizeEl;
38403
38404         ts.innerHTML = '';
38405         ts.appendChild(document.createTextNode(v));
38406         v = ts.innerHTML;
38407
38408         Roo.fly(ts).setWidth(this.el.getWidth());
38409         if(v.length < 1){
38410             v = "&#160;&#160;";
38411         }else{
38412             if(Roo.isIE){
38413                 v = v.replace(/\n/g, '<p>&#160;</p>');
38414             }
38415             v += "&#160;\n&#160;";
38416         }
38417         ts.innerHTML = v;
38418         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38419         if(h != this.lastHeight){
38420             this.lastHeight = h;
38421             this.el.setHeight(h);
38422             this.fireEvent("autosize", this, h);
38423         }
38424     }
38425 });/*
38426  * Based on:
38427  * Ext JS Library 1.1.1
38428  * Copyright(c) 2006-2007, Ext JS, LLC.
38429  *
38430  * Originally Released Under LGPL - original licence link has changed is not relivant.
38431  *
38432  * Fork - LGPL
38433  * <script type="text/javascript">
38434  */
38435  
38436
38437 /**
38438  * @class Roo.form.NumberField
38439  * @extends Roo.form.TextField
38440  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38441  * @constructor
38442  * Creates a new NumberField
38443  * @param {Object} config Configuration options
38444  */
38445 Roo.form.NumberField = function(config){
38446     Roo.form.NumberField.superclass.constructor.call(this, config);
38447 };
38448
38449 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38450     /**
38451      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38452      */
38453     fieldClass: "x-form-field x-form-num-field",
38454     /**
38455      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38456      */
38457     allowDecimals : true,
38458     /**
38459      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38460      */
38461     decimalSeparator : ".",
38462     /**
38463      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38464      */
38465     decimalPrecision : 2,
38466     /**
38467      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38468      */
38469     allowNegative : true,
38470     /**
38471      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38472      */
38473     minValue : Number.NEGATIVE_INFINITY,
38474     /**
38475      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38476      */
38477     maxValue : Number.MAX_VALUE,
38478     /**
38479      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38480      */
38481     minText : "The minimum value for this field is {0}",
38482     /**
38483      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38484      */
38485     maxText : "The maximum value for this field is {0}",
38486     /**
38487      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38488      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38489      */
38490     nanText : "{0} is not a valid number",
38491
38492     // private
38493     initEvents : function(){
38494         Roo.form.NumberField.superclass.initEvents.call(this);
38495         var allowed = "0123456789";
38496         if(this.allowDecimals){
38497             allowed += this.decimalSeparator;
38498         }
38499         if(this.allowNegative){
38500             allowed += "-";
38501         }
38502         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38503         var keyPress = function(e){
38504             var k = e.getKey();
38505             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38506                 return;
38507             }
38508             var c = e.getCharCode();
38509             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38510                 e.stopEvent();
38511             }
38512         };
38513         this.el.on("keypress", keyPress, this);
38514     },
38515
38516     // private
38517     validateValue : function(value){
38518         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38519             return false;
38520         }
38521         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38522              return true;
38523         }
38524         var num = this.parseValue(value);
38525         if(isNaN(num)){
38526             this.markInvalid(String.format(this.nanText, value));
38527             return false;
38528         }
38529         if(num < this.minValue){
38530             this.markInvalid(String.format(this.minText, this.minValue));
38531             return false;
38532         }
38533         if(num > this.maxValue){
38534             this.markInvalid(String.format(this.maxText, this.maxValue));
38535             return false;
38536         }
38537         return true;
38538     },
38539
38540     getValue : function(){
38541         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38542     },
38543
38544     // private
38545     parseValue : function(value){
38546         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38547         return isNaN(value) ? '' : value;
38548     },
38549
38550     // private
38551     fixPrecision : function(value){
38552         var nan = isNaN(value);
38553         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38554             return nan ? '' : value;
38555         }
38556         return parseFloat(value).toFixed(this.decimalPrecision);
38557     },
38558
38559     setValue : function(v){
38560         v = this.fixPrecision(v);
38561         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38562     },
38563
38564     // private
38565     decimalPrecisionFcn : function(v){
38566         return Math.floor(v);
38567     },
38568
38569     beforeBlur : function(){
38570         var v = this.parseValue(this.getRawValue());
38571         if(v){
38572             this.setValue(v);
38573         }
38574     }
38575 });/*
38576  * Based on:
38577  * Ext JS Library 1.1.1
38578  * Copyright(c) 2006-2007, Ext JS, LLC.
38579  *
38580  * Originally Released Under LGPL - original licence link has changed is not relivant.
38581  *
38582  * Fork - LGPL
38583  * <script type="text/javascript">
38584  */
38585  
38586 /**
38587  * @class Roo.form.DateField
38588  * @extends Roo.form.TriggerField
38589  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38590 * @constructor
38591 * Create a new DateField
38592 * @param {Object} config
38593  */
38594 Roo.form.DateField = function(config){
38595     Roo.form.DateField.superclass.constructor.call(this, config);
38596     
38597       this.addEvents({
38598          
38599         /**
38600          * @event select
38601          * Fires when a date is selected
38602              * @param {Roo.form.DateField} combo This combo box
38603              * @param {Date} date The date selected
38604              */
38605         'select' : true
38606          
38607     });
38608     
38609     
38610     if(typeof this.minValue == "string") {
38611         this.minValue = this.parseDate(this.minValue);
38612     }
38613     if(typeof this.maxValue == "string") {
38614         this.maxValue = this.parseDate(this.maxValue);
38615     }
38616     this.ddMatch = null;
38617     if(this.disabledDates){
38618         var dd = this.disabledDates;
38619         var re = "(?:";
38620         for(var i = 0; i < dd.length; i++){
38621             re += dd[i];
38622             if(i != dd.length-1) {
38623                 re += "|";
38624             }
38625         }
38626         this.ddMatch = new RegExp(re + ")");
38627     }
38628 };
38629
38630 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38631     /**
38632      * @cfg {String} format
38633      * The default date format string which can be overriden for localization support.  The format must be
38634      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38635      */
38636     format : "m/d/y",
38637     /**
38638      * @cfg {String} altFormats
38639      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38640      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38641      */
38642     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38643     /**
38644      * @cfg {Array} disabledDays
38645      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38646      */
38647     disabledDays : null,
38648     /**
38649      * @cfg {String} disabledDaysText
38650      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38651      */
38652     disabledDaysText : "Disabled",
38653     /**
38654      * @cfg {Array} disabledDates
38655      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38656      * expression so they are very powerful. Some examples:
38657      * <ul>
38658      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38659      * <li>["03/08", "09/16"] would disable those days for every year</li>
38660      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38661      * <li>["03/../2006"] would disable every day in March 2006</li>
38662      * <li>["^03"] would disable every day in every March</li>
38663      * </ul>
38664      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38665      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38666      */
38667     disabledDates : null,
38668     /**
38669      * @cfg {String} disabledDatesText
38670      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38671      */
38672     disabledDatesText : "Disabled",
38673     /**
38674      * @cfg {Date/String} minValue
38675      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38676      * valid format (defaults to null).
38677      */
38678     minValue : null,
38679     /**
38680      * @cfg {Date/String} maxValue
38681      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38682      * valid format (defaults to null).
38683      */
38684     maxValue : null,
38685     /**
38686      * @cfg {String} minText
38687      * The error text to display when the date in the cell is before minValue (defaults to
38688      * 'The date in this field must be after {minValue}').
38689      */
38690     minText : "The date in this field must be equal to or after {0}",
38691     /**
38692      * @cfg {String} maxText
38693      * The error text to display when the date in the cell is after maxValue (defaults to
38694      * 'The date in this field must be before {maxValue}').
38695      */
38696     maxText : "The date in this field must be equal to or before {0}",
38697     /**
38698      * @cfg {String} invalidText
38699      * The error text to display when the date in the field is invalid (defaults to
38700      * '{value} is not a valid date - it must be in the format {format}').
38701      */
38702     invalidText : "{0} is not a valid date - it must be in the format {1}",
38703     /**
38704      * @cfg {String} triggerClass
38705      * An additional CSS class used to style the trigger button.  The trigger will always get the
38706      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38707      * which displays a calendar icon).
38708      */
38709     triggerClass : 'x-form-date-trigger',
38710     
38711
38712     /**
38713      * @cfg {Boolean} useIso
38714      * if enabled, then the date field will use a hidden field to store the 
38715      * real value as iso formated date. default (false)
38716      */ 
38717     useIso : false,
38718     /**
38719      * @cfg {String/Object} autoCreate
38720      * A DomHelper element spec, or true for a default element spec (defaults to
38721      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38722      */ 
38723     // private
38724     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38725     
38726     // private
38727     hiddenField: false,
38728     
38729     onRender : function(ct, position)
38730     {
38731         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38732         if (this.useIso) {
38733             //this.el.dom.removeAttribute('name'); 
38734             Roo.log("Changing name?");
38735             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38736             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38737                     'before', true);
38738             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38739             // prevent input submission
38740             this.hiddenName = this.name;
38741         }
38742             
38743             
38744     },
38745     
38746     // private
38747     validateValue : function(value)
38748     {
38749         value = this.formatDate(value);
38750         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38751             Roo.log('super failed');
38752             return false;
38753         }
38754         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38755              return true;
38756         }
38757         var svalue = value;
38758         value = this.parseDate(value);
38759         if(!value){
38760             Roo.log('parse date failed' + svalue);
38761             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38762             return false;
38763         }
38764         var time = value.getTime();
38765         if(this.minValue && time < this.minValue.getTime()){
38766             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38767             return false;
38768         }
38769         if(this.maxValue && time > this.maxValue.getTime()){
38770             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38771             return false;
38772         }
38773         if(this.disabledDays){
38774             var day = value.getDay();
38775             for(var i = 0; i < this.disabledDays.length; i++) {
38776                 if(day === this.disabledDays[i]){
38777                     this.markInvalid(this.disabledDaysText);
38778                     return false;
38779                 }
38780             }
38781         }
38782         var fvalue = this.formatDate(value);
38783         if(this.ddMatch && this.ddMatch.test(fvalue)){
38784             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38785             return false;
38786         }
38787         return true;
38788     },
38789
38790     // private
38791     // Provides logic to override the default TriggerField.validateBlur which just returns true
38792     validateBlur : function(){
38793         return !this.menu || !this.menu.isVisible();
38794     },
38795     
38796     getName: function()
38797     {
38798         // returns hidden if it's set..
38799         if (!this.rendered) {return ''};
38800         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38801         
38802     },
38803
38804     /**
38805      * Returns the current date value of the date field.
38806      * @return {Date} The date value
38807      */
38808     getValue : function(){
38809         
38810         return  this.hiddenField ?
38811                 this.hiddenField.value :
38812                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38813     },
38814
38815     /**
38816      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38817      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38818      * (the default format used is "m/d/y").
38819      * <br />Usage:
38820      * <pre><code>
38821 //All of these calls set the same date value (May 4, 2006)
38822
38823 //Pass a date object:
38824 var dt = new Date('5/4/06');
38825 dateField.setValue(dt);
38826
38827 //Pass a date string (default format):
38828 dateField.setValue('5/4/06');
38829
38830 //Pass a date string (custom format):
38831 dateField.format = 'Y-m-d';
38832 dateField.setValue('2006-5-4');
38833 </code></pre>
38834      * @param {String/Date} date The date or valid date string
38835      */
38836     setValue : function(date){
38837         if (this.hiddenField) {
38838             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38839         }
38840         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38841         // make sure the value field is always stored as a date..
38842         this.value = this.parseDate(date);
38843         
38844         
38845     },
38846
38847     // private
38848     parseDate : function(value){
38849         if(!value || value instanceof Date){
38850             return value;
38851         }
38852         var v = Date.parseDate(value, this.format);
38853          if (!v && this.useIso) {
38854             v = Date.parseDate(value, 'Y-m-d');
38855         }
38856         if(!v && this.altFormats){
38857             if(!this.altFormatsArray){
38858                 this.altFormatsArray = this.altFormats.split("|");
38859             }
38860             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38861                 v = Date.parseDate(value, this.altFormatsArray[i]);
38862             }
38863         }
38864         return v;
38865     },
38866
38867     // private
38868     formatDate : function(date, fmt){
38869         return (!date || !(date instanceof Date)) ?
38870                date : date.dateFormat(fmt || this.format);
38871     },
38872
38873     // private
38874     menuListeners : {
38875         select: function(m, d){
38876             
38877             this.setValue(d);
38878             this.fireEvent('select', this, d);
38879         },
38880         show : function(){ // retain focus styling
38881             this.onFocus();
38882         },
38883         hide : function(){
38884             this.focus.defer(10, this);
38885             var ml = this.menuListeners;
38886             this.menu.un("select", ml.select,  this);
38887             this.menu.un("show", ml.show,  this);
38888             this.menu.un("hide", ml.hide,  this);
38889         }
38890     },
38891
38892     // private
38893     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38894     onTriggerClick : function(){
38895         if(this.disabled){
38896             return;
38897         }
38898         if(this.menu == null){
38899             this.menu = new Roo.menu.DateMenu();
38900         }
38901         Roo.apply(this.menu.picker,  {
38902             showClear: this.allowBlank,
38903             minDate : this.minValue,
38904             maxDate : this.maxValue,
38905             disabledDatesRE : this.ddMatch,
38906             disabledDatesText : this.disabledDatesText,
38907             disabledDays : this.disabledDays,
38908             disabledDaysText : this.disabledDaysText,
38909             format : this.useIso ? 'Y-m-d' : this.format,
38910             minText : String.format(this.minText, this.formatDate(this.minValue)),
38911             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38912         });
38913         this.menu.on(Roo.apply({}, this.menuListeners, {
38914             scope:this
38915         }));
38916         this.menu.picker.setValue(this.getValue() || new Date());
38917         this.menu.show(this.el, "tl-bl?");
38918     },
38919
38920     beforeBlur : function(){
38921         var v = this.parseDate(this.getRawValue());
38922         if(v){
38923             this.setValue(v);
38924         }
38925     },
38926
38927     /*@
38928      * overide
38929      * 
38930      */
38931     isDirty : function() {
38932         if(this.disabled) {
38933             return false;
38934         }
38935         
38936         if(typeof(this.startValue) === 'undefined'){
38937             return false;
38938         }
38939         
38940         return String(this.getValue()) !== String(this.startValue);
38941         
38942     }
38943 });/*
38944  * Based on:
38945  * Ext JS Library 1.1.1
38946  * Copyright(c) 2006-2007, Ext JS, LLC.
38947  *
38948  * Originally Released Under LGPL - original licence link has changed is not relivant.
38949  *
38950  * Fork - LGPL
38951  * <script type="text/javascript">
38952  */
38953  
38954 /**
38955  * @class Roo.form.MonthField
38956  * @extends Roo.form.TriggerField
38957  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38958 * @constructor
38959 * Create a new MonthField
38960 * @param {Object} config
38961  */
38962 Roo.form.MonthField = function(config){
38963     
38964     Roo.form.MonthField.superclass.constructor.call(this, config);
38965     
38966       this.addEvents({
38967          
38968         /**
38969          * @event select
38970          * Fires when a date is selected
38971              * @param {Roo.form.MonthFieeld} combo This combo box
38972              * @param {Date} date The date selected
38973              */
38974         'select' : true
38975          
38976     });
38977     
38978     
38979     if(typeof this.minValue == "string") {
38980         this.minValue = this.parseDate(this.minValue);
38981     }
38982     if(typeof this.maxValue == "string") {
38983         this.maxValue = this.parseDate(this.maxValue);
38984     }
38985     this.ddMatch = null;
38986     if(this.disabledDates){
38987         var dd = this.disabledDates;
38988         var re = "(?:";
38989         for(var i = 0; i < dd.length; i++){
38990             re += dd[i];
38991             if(i != dd.length-1) {
38992                 re += "|";
38993             }
38994         }
38995         this.ddMatch = new RegExp(re + ")");
38996     }
38997 };
38998
38999 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
39000     /**
39001      * @cfg {String} format
39002      * The default date format string which can be overriden for localization support.  The format must be
39003      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
39004      */
39005     format : "M Y",
39006     /**
39007      * @cfg {String} altFormats
39008      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
39009      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
39010      */
39011     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
39012     /**
39013      * @cfg {Array} disabledDays
39014      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
39015      */
39016     disabledDays : [0,1,2,3,4,5,6],
39017     /**
39018      * @cfg {String} disabledDaysText
39019      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
39020      */
39021     disabledDaysText : "Disabled",
39022     /**
39023      * @cfg {Array} disabledDates
39024      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
39025      * expression so they are very powerful. Some examples:
39026      * <ul>
39027      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
39028      * <li>["03/08", "09/16"] would disable those days for every year</li>
39029      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
39030      * <li>["03/../2006"] would disable every day in March 2006</li>
39031      * <li>["^03"] would disable every day in every March</li>
39032      * </ul>
39033      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
39034      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
39035      */
39036     disabledDates : null,
39037     /**
39038      * @cfg {String} disabledDatesText
39039      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39040      */
39041     disabledDatesText : "Disabled",
39042     /**
39043      * @cfg {Date/String} minValue
39044      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39045      * valid format (defaults to null).
39046      */
39047     minValue : null,
39048     /**
39049      * @cfg {Date/String} maxValue
39050      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39051      * valid format (defaults to null).
39052      */
39053     maxValue : null,
39054     /**
39055      * @cfg {String} minText
39056      * The error text to display when the date in the cell is before minValue (defaults to
39057      * 'The date in this field must be after {minValue}').
39058      */
39059     minText : "The date in this field must be equal to or after {0}",
39060     /**
39061      * @cfg {String} maxTextf
39062      * The error text to display when the date in the cell is after maxValue (defaults to
39063      * 'The date in this field must be before {maxValue}').
39064      */
39065     maxText : "The date in this field must be equal to or before {0}",
39066     /**
39067      * @cfg {String} invalidText
39068      * The error text to display when the date in the field is invalid (defaults to
39069      * '{value} is not a valid date - it must be in the format {format}').
39070      */
39071     invalidText : "{0} is not a valid date - it must be in the format {1}",
39072     /**
39073      * @cfg {String} triggerClass
39074      * An additional CSS class used to style the trigger button.  The trigger will always get the
39075      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39076      * which displays a calendar icon).
39077      */
39078     triggerClass : 'x-form-date-trigger',
39079     
39080
39081     /**
39082      * @cfg {Boolean} useIso
39083      * if enabled, then the date field will use a hidden field to store the 
39084      * real value as iso formated date. default (true)
39085      */ 
39086     useIso : true,
39087     /**
39088      * @cfg {String/Object} autoCreate
39089      * A DomHelper element spec, or true for a default element spec (defaults to
39090      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39091      */ 
39092     // private
39093     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39094     
39095     // private
39096     hiddenField: false,
39097     
39098     hideMonthPicker : false,
39099     
39100     onRender : function(ct, position)
39101     {
39102         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39103         if (this.useIso) {
39104             this.el.dom.removeAttribute('name'); 
39105             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39106                     'before', true);
39107             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39108             // prevent input submission
39109             this.hiddenName = this.name;
39110         }
39111             
39112             
39113     },
39114     
39115     // private
39116     validateValue : function(value)
39117     {
39118         value = this.formatDate(value);
39119         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39120             return false;
39121         }
39122         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39123              return true;
39124         }
39125         var svalue = value;
39126         value = this.parseDate(value);
39127         if(!value){
39128             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39129             return false;
39130         }
39131         var time = value.getTime();
39132         if(this.minValue && time < this.minValue.getTime()){
39133             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39134             return false;
39135         }
39136         if(this.maxValue && time > this.maxValue.getTime()){
39137             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39138             return false;
39139         }
39140         /*if(this.disabledDays){
39141             var day = value.getDay();
39142             for(var i = 0; i < this.disabledDays.length; i++) {
39143                 if(day === this.disabledDays[i]){
39144                     this.markInvalid(this.disabledDaysText);
39145                     return false;
39146                 }
39147             }
39148         }
39149         */
39150         var fvalue = this.formatDate(value);
39151         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39152             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39153             return false;
39154         }
39155         */
39156         return true;
39157     },
39158
39159     // private
39160     // Provides logic to override the default TriggerField.validateBlur which just returns true
39161     validateBlur : function(){
39162         return !this.menu || !this.menu.isVisible();
39163     },
39164
39165     /**
39166      * Returns the current date value of the date field.
39167      * @return {Date} The date value
39168      */
39169     getValue : function(){
39170         
39171         
39172         
39173         return  this.hiddenField ?
39174                 this.hiddenField.value :
39175                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39176     },
39177
39178     /**
39179      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39180      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39181      * (the default format used is "m/d/y").
39182      * <br />Usage:
39183      * <pre><code>
39184 //All of these calls set the same date value (May 4, 2006)
39185
39186 //Pass a date object:
39187 var dt = new Date('5/4/06');
39188 monthField.setValue(dt);
39189
39190 //Pass a date string (default format):
39191 monthField.setValue('5/4/06');
39192
39193 //Pass a date string (custom format):
39194 monthField.format = 'Y-m-d';
39195 monthField.setValue('2006-5-4');
39196 </code></pre>
39197      * @param {String/Date} date The date or valid date string
39198      */
39199     setValue : function(date){
39200         Roo.log('month setValue' + date);
39201         // can only be first of month..
39202         
39203         var val = this.parseDate(date);
39204         
39205         if (this.hiddenField) {
39206             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39207         }
39208         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39209         this.value = this.parseDate(date);
39210     },
39211
39212     // private
39213     parseDate : function(value){
39214         if(!value || value instanceof Date){
39215             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39216             return value;
39217         }
39218         var v = Date.parseDate(value, this.format);
39219         if (!v && this.useIso) {
39220             v = Date.parseDate(value, 'Y-m-d');
39221         }
39222         if (v) {
39223             // 
39224             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39225         }
39226         
39227         
39228         if(!v && this.altFormats){
39229             if(!this.altFormatsArray){
39230                 this.altFormatsArray = this.altFormats.split("|");
39231             }
39232             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39233                 v = Date.parseDate(value, this.altFormatsArray[i]);
39234             }
39235         }
39236         return v;
39237     },
39238
39239     // private
39240     formatDate : function(date, fmt){
39241         return (!date || !(date instanceof Date)) ?
39242                date : date.dateFormat(fmt || this.format);
39243     },
39244
39245     // private
39246     menuListeners : {
39247         select: function(m, d){
39248             this.setValue(d);
39249             this.fireEvent('select', this, d);
39250         },
39251         show : function(){ // retain focus styling
39252             this.onFocus();
39253         },
39254         hide : function(){
39255             this.focus.defer(10, this);
39256             var ml = this.menuListeners;
39257             this.menu.un("select", ml.select,  this);
39258             this.menu.un("show", ml.show,  this);
39259             this.menu.un("hide", ml.hide,  this);
39260         }
39261     },
39262     // private
39263     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39264     onTriggerClick : function(){
39265         if(this.disabled){
39266             return;
39267         }
39268         if(this.menu == null){
39269             this.menu = new Roo.menu.DateMenu();
39270            
39271         }
39272         
39273         Roo.apply(this.menu.picker,  {
39274             
39275             showClear: this.allowBlank,
39276             minDate : this.minValue,
39277             maxDate : this.maxValue,
39278             disabledDatesRE : this.ddMatch,
39279             disabledDatesText : this.disabledDatesText,
39280             
39281             format : this.useIso ? 'Y-m-d' : this.format,
39282             minText : String.format(this.minText, this.formatDate(this.minValue)),
39283             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39284             
39285         });
39286          this.menu.on(Roo.apply({}, this.menuListeners, {
39287             scope:this
39288         }));
39289        
39290         
39291         var m = this.menu;
39292         var p = m.picker;
39293         
39294         // hide month picker get's called when we called by 'before hide';
39295         
39296         var ignorehide = true;
39297         p.hideMonthPicker  = function(disableAnim){
39298             if (ignorehide) {
39299                 return;
39300             }
39301              if(this.monthPicker){
39302                 Roo.log("hideMonthPicker called");
39303                 if(disableAnim === true){
39304                     this.monthPicker.hide();
39305                 }else{
39306                     this.monthPicker.slideOut('t', {duration:.2});
39307                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39308                     p.fireEvent("select", this, this.value);
39309                     m.hide();
39310                 }
39311             }
39312         }
39313         
39314         Roo.log('picker set value');
39315         Roo.log(this.getValue());
39316         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39317         m.show(this.el, 'tl-bl?');
39318         ignorehide  = false;
39319         // this will trigger hideMonthPicker..
39320         
39321         
39322         // hidden the day picker
39323         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39324         
39325         
39326         
39327       
39328         
39329         p.showMonthPicker.defer(100, p);
39330     
39331         
39332        
39333     },
39334
39335     beforeBlur : function(){
39336         var v = this.parseDate(this.getRawValue());
39337         if(v){
39338             this.setValue(v);
39339         }
39340     }
39341
39342     /** @cfg {Boolean} grow @hide */
39343     /** @cfg {Number} growMin @hide */
39344     /** @cfg {Number} growMax @hide */
39345     /**
39346      * @hide
39347      * @method autoSize
39348      */
39349 });/*
39350  * Based on:
39351  * Ext JS Library 1.1.1
39352  * Copyright(c) 2006-2007, Ext JS, LLC.
39353  *
39354  * Originally Released Under LGPL - original licence link has changed is not relivant.
39355  *
39356  * Fork - LGPL
39357  * <script type="text/javascript">
39358  */
39359  
39360
39361 /**
39362  * @class Roo.form.ComboBox
39363  * @extends Roo.form.TriggerField
39364  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39365  * @constructor
39366  * Create a new ComboBox.
39367  * @param {Object} config Configuration options
39368  */
39369 Roo.form.ComboBox = function(config){
39370     Roo.form.ComboBox.superclass.constructor.call(this, config);
39371     this.addEvents({
39372         /**
39373          * @event expand
39374          * Fires when the dropdown list is expanded
39375              * @param {Roo.form.ComboBox} combo This combo box
39376              */
39377         'expand' : true,
39378         /**
39379          * @event collapse
39380          * Fires when the dropdown list is collapsed
39381              * @param {Roo.form.ComboBox} combo This combo box
39382              */
39383         'collapse' : true,
39384         /**
39385          * @event beforeselect
39386          * Fires before a list item is selected. Return false to cancel the selection.
39387              * @param {Roo.form.ComboBox} combo This combo box
39388              * @param {Roo.data.Record} record The data record returned from the underlying store
39389              * @param {Number} index The index of the selected item in the dropdown list
39390              */
39391         'beforeselect' : true,
39392         /**
39393          * @event select
39394          * Fires when a list item is selected
39395              * @param {Roo.form.ComboBox} combo This combo box
39396              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39397              * @param {Number} index The index of the selected item in the dropdown list
39398              */
39399         'select' : true,
39400         /**
39401          * @event beforequery
39402          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39403          * The event object passed has these properties:
39404              * @param {Roo.form.ComboBox} combo This combo box
39405              * @param {String} query The query
39406              * @param {Boolean} forceAll true to force "all" query
39407              * @param {Boolean} cancel true to cancel the query
39408              * @param {Object} e The query event object
39409              */
39410         'beforequery': true,
39411          /**
39412          * @event add
39413          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39414              * @param {Roo.form.ComboBox} combo This combo box
39415              */
39416         'add' : true,
39417         /**
39418          * @event edit
39419          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39420              * @param {Roo.form.ComboBox} combo This combo box
39421              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39422              */
39423         'edit' : true
39424         
39425         
39426     });
39427     if(this.transform){
39428         this.allowDomMove = false;
39429         var s = Roo.getDom(this.transform);
39430         if(!this.hiddenName){
39431             this.hiddenName = s.name;
39432         }
39433         if(!this.store){
39434             this.mode = 'local';
39435             var d = [], opts = s.options;
39436             for(var i = 0, len = opts.length;i < len; i++){
39437                 var o = opts[i];
39438                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39439                 if(o.selected) {
39440                     this.value = value;
39441                 }
39442                 d.push([value, o.text]);
39443             }
39444             this.store = new Roo.data.SimpleStore({
39445                 'id': 0,
39446                 fields: ['value', 'text'],
39447                 data : d
39448             });
39449             this.valueField = 'value';
39450             this.displayField = 'text';
39451         }
39452         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39453         if(!this.lazyRender){
39454             this.target = true;
39455             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39456             s.parentNode.removeChild(s); // remove it
39457             this.render(this.el.parentNode);
39458         }else{
39459             s.parentNode.removeChild(s); // remove it
39460         }
39461
39462     }
39463     if (this.store) {
39464         this.store = Roo.factory(this.store, Roo.data);
39465     }
39466     
39467     this.selectedIndex = -1;
39468     if(this.mode == 'local'){
39469         if(config.queryDelay === undefined){
39470             this.queryDelay = 10;
39471         }
39472         if(config.minChars === undefined){
39473             this.minChars = 0;
39474         }
39475     }
39476 };
39477
39478 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39479     /**
39480      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39481      */
39482     /**
39483      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39484      * rendering into an Roo.Editor, defaults to false)
39485      */
39486     /**
39487      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39488      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39489      */
39490     /**
39491      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39492      */
39493     /**
39494      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39495      * the dropdown list (defaults to undefined, with no header element)
39496      */
39497
39498      /**
39499      * @cfg {String/Roo.Template} tpl The template to use to render the output
39500      */
39501      
39502     // private
39503     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39504     /**
39505      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39506      */
39507     listWidth: undefined,
39508     /**
39509      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39510      * mode = 'remote' or 'text' if mode = 'local')
39511      */
39512     displayField: undefined,
39513     /**
39514      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39515      * mode = 'remote' or 'value' if mode = 'local'). 
39516      * Note: use of a valueField requires the user make a selection
39517      * in order for a value to be mapped.
39518      */
39519     valueField: undefined,
39520     
39521     
39522     /**
39523      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39524      * field's data value (defaults to the underlying DOM element's name)
39525      */
39526     hiddenName: undefined,
39527     /**
39528      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39529      */
39530     listClass: '',
39531     /**
39532      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39533      */
39534     selectedClass: 'x-combo-selected',
39535     /**
39536      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39537      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39538      * which displays a downward arrow icon).
39539      */
39540     triggerClass : 'x-form-arrow-trigger',
39541     /**
39542      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39543      */
39544     shadow:'sides',
39545     /**
39546      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39547      * anchor positions (defaults to 'tl-bl')
39548      */
39549     listAlign: 'tl-bl?',
39550     /**
39551      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39552      */
39553     maxHeight: 300,
39554     /**
39555      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39556      * query specified by the allQuery config option (defaults to 'query')
39557      */
39558     triggerAction: 'query',
39559     /**
39560      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39561      * (defaults to 4, does not apply if editable = false)
39562      */
39563     minChars : 4,
39564     /**
39565      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39566      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39567      */
39568     typeAhead: false,
39569     /**
39570      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39571      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39572      */
39573     queryDelay: 500,
39574     /**
39575      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39576      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39577      */
39578     pageSize: 0,
39579     /**
39580      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39581      * when editable = true (defaults to false)
39582      */
39583     selectOnFocus:false,
39584     /**
39585      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39586      */
39587     queryParam: 'query',
39588     /**
39589      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39590      * when mode = 'remote' (defaults to 'Loading...')
39591      */
39592     loadingText: 'Loading...',
39593     /**
39594      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39595      */
39596     resizable: false,
39597     /**
39598      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39599      */
39600     handleHeight : 8,
39601     /**
39602      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39603      * traditional select (defaults to true)
39604      */
39605     editable: true,
39606     /**
39607      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39608      */
39609     allQuery: '',
39610     /**
39611      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39612      */
39613     mode: 'remote',
39614     /**
39615      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39616      * listWidth has a higher value)
39617      */
39618     minListWidth : 70,
39619     /**
39620      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39621      * allow the user to set arbitrary text into the field (defaults to false)
39622      */
39623     forceSelection:false,
39624     /**
39625      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39626      * if typeAhead = true (defaults to 250)
39627      */
39628     typeAheadDelay : 250,
39629     /**
39630      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39631      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39632      */
39633     valueNotFoundText : undefined,
39634     /**
39635      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39636      */
39637     blockFocus : false,
39638     
39639     /**
39640      * @cfg {Boolean} disableClear Disable showing of clear button.
39641      */
39642     disableClear : false,
39643     /**
39644      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39645      */
39646     alwaysQuery : false,
39647     
39648     //private
39649     addicon : false,
39650     editicon: false,
39651     
39652     // element that contains real text value.. (when hidden is used..)
39653      
39654     // private
39655     onRender : function(ct, position){
39656         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39657         if(this.hiddenName){
39658             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39659                     'before', true);
39660             this.hiddenField.value =
39661                 this.hiddenValue !== undefined ? this.hiddenValue :
39662                 this.value !== undefined ? this.value : '';
39663
39664             // prevent input submission
39665             this.el.dom.removeAttribute('name');
39666              
39667              
39668         }
39669         if(Roo.isGecko){
39670             this.el.dom.setAttribute('autocomplete', 'off');
39671         }
39672
39673         var cls = 'x-combo-list';
39674
39675         this.list = new Roo.Layer({
39676             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39677         });
39678
39679         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39680         this.list.setWidth(lw);
39681         this.list.swallowEvent('mousewheel');
39682         this.assetHeight = 0;
39683
39684         if(this.title){
39685             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39686             this.assetHeight += this.header.getHeight();
39687         }
39688
39689         this.innerList = this.list.createChild({cls:cls+'-inner'});
39690         this.innerList.on('mouseover', this.onViewOver, this);
39691         this.innerList.on('mousemove', this.onViewMove, this);
39692         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39693         
39694         if(this.allowBlank && !this.pageSize && !this.disableClear){
39695             this.footer = this.list.createChild({cls:cls+'-ft'});
39696             this.pageTb = new Roo.Toolbar(this.footer);
39697            
39698         }
39699         if(this.pageSize){
39700             this.footer = this.list.createChild({cls:cls+'-ft'});
39701             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39702                     {pageSize: this.pageSize});
39703             
39704         }
39705         
39706         if (this.pageTb && this.allowBlank && !this.disableClear) {
39707             var _this = this;
39708             this.pageTb.add(new Roo.Toolbar.Fill(), {
39709                 cls: 'x-btn-icon x-btn-clear',
39710                 text: '&#160;',
39711                 handler: function()
39712                 {
39713                     _this.collapse();
39714                     _this.clearValue();
39715                     _this.onSelect(false, -1);
39716                 }
39717             });
39718         }
39719         if (this.footer) {
39720             this.assetHeight += this.footer.getHeight();
39721         }
39722         
39723
39724         if(!this.tpl){
39725             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39726         }
39727
39728         this.view = new Roo.View(this.innerList, this.tpl, {
39729             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39730         });
39731
39732         this.view.on('click', this.onViewClick, this);
39733
39734         this.store.on('beforeload', this.onBeforeLoad, this);
39735         this.store.on('load', this.onLoad, this);
39736         this.store.on('loadexception', this.onLoadException, this);
39737
39738         if(this.resizable){
39739             this.resizer = new Roo.Resizable(this.list,  {
39740                pinned:true, handles:'se'
39741             });
39742             this.resizer.on('resize', function(r, w, h){
39743                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39744                 this.listWidth = w;
39745                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39746                 this.restrictHeight();
39747             }, this);
39748             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39749         }
39750         if(!this.editable){
39751             this.editable = true;
39752             this.setEditable(false);
39753         }  
39754         
39755         
39756         if (typeof(this.events.add.listeners) != 'undefined') {
39757             
39758             this.addicon = this.wrap.createChild(
39759                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39760        
39761             this.addicon.on('click', function(e) {
39762                 this.fireEvent('add', this);
39763             }, this);
39764         }
39765         if (typeof(this.events.edit.listeners) != 'undefined') {
39766             
39767             this.editicon = this.wrap.createChild(
39768                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39769             if (this.addicon) {
39770                 this.editicon.setStyle('margin-left', '40px');
39771             }
39772             this.editicon.on('click', function(e) {
39773                 
39774                 // we fire even  if inothing is selected..
39775                 this.fireEvent('edit', this, this.lastData );
39776                 
39777             }, this);
39778         }
39779         
39780         
39781         
39782     },
39783
39784     // private
39785     initEvents : function(){
39786         Roo.form.ComboBox.superclass.initEvents.call(this);
39787
39788         this.keyNav = new Roo.KeyNav(this.el, {
39789             "up" : function(e){
39790                 this.inKeyMode = true;
39791                 this.selectPrev();
39792             },
39793
39794             "down" : function(e){
39795                 if(!this.isExpanded()){
39796                     this.onTriggerClick();
39797                 }else{
39798                     this.inKeyMode = true;
39799                     this.selectNext();
39800                 }
39801             },
39802
39803             "enter" : function(e){
39804                 this.onViewClick();
39805                 //return true;
39806             },
39807
39808             "esc" : function(e){
39809                 this.collapse();
39810             },
39811
39812             "tab" : function(e){
39813                 this.onViewClick(false);
39814                 this.fireEvent("specialkey", this, e);
39815                 return true;
39816             },
39817
39818             scope : this,
39819
39820             doRelay : function(foo, bar, hname){
39821                 if(hname == 'down' || this.scope.isExpanded()){
39822                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39823                 }
39824                 return true;
39825             },
39826
39827             forceKeyDown: true
39828         });
39829         this.queryDelay = Math.max(this.queryDelay || 10,
39830                 this.mode == 'local' ? 10 : 250);
39831         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39832         if(this.typeAhead){
39833             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39834         }
39835         if(this.editable !== false){
39836             this.el.on("keyup", this.onKeyUp, this);
39837         }
39838         if(this.forceSelection){
39839             this.on('blur', this.doForce, this);
39840         }
39841     },
39842
39843     onDestroy : function(){
39844         if(this.view){
39845             this.view.setStore(null);
39846             this.view.el.removeAllListeners();
39847             this.view.el.remove();
39848             this.view.purgeListeners();
39849         }
39850         if(this.list){
39851             this.list.destroy();
39852         }
39853         if(this.store){
39854             this.store.un('beforeload', this.onBeforeLoad, this);
39855             this.store.un('load', this.onLoad, this);
39856             this.store.un('loadexception', this.onLoadException, this);
39857         }
39858         Roo.form.ComboBox.superclass.onDestroy.call(this);
39859     },
39860
39861     // private
39862     fireKey : function(e){
39863         if(e.isNavKeyPress() && !this.list.isVisible()){
39864             this.fireEvent("specialkey", this, e);
39865         }
39866     },
39867
39868     // private
39869     onResize: function(w, h){
39870         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39871         
39872         if(typeof w != 'number'){
39873             // we do not handle it!?!?
39874             return;
39875         }
39876         var tw = this.trigger.getWidth();
39877         tw += this.addicon ? this.addicon.getWidth() : 0;
39878         tw += this.editicon ? this.editicon.getWidth() : 0;
39879         var x = w - tw;
39880         this.el.setWidth( this.adjustWidth('input', x));
39881             
39882         this.trigger.setStyle('left', x+'px');
39883         
39884         if(this.list && this.listWidth === undefined){
39885             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39886             this.list.setWidth(lw);
39887             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39888         }
39889         
39890     
39891         
39892     },
39893
39894     /**
39895      * Allow or prevent the user from directly editing the field text.  If false is passed,
39896      * the user will only be able to select from the items defined in the dropdown list.  This method
39897      * is the runtime equivalent of setting the 'editable' config option at config time.
39898      * @param {Boolean} value True to allow the user to directly edit the field text
39899      */
39900     setEditable : function(value){
39901         if(value == this.editable){
39902             return;
39903         }
39904         this.editable = value;
39905         if(!value){
39906             this.el.dom.setAttribute('readOnly', true);
39907             this.el.on('mousedown', this.onTriggerClick,  this);
39908             this.el.addClass('x-combo-noedit');
39909         }else{
39910             this.el.dom.setAttribute('readOnly', false);
39911             this.el.un('mousedown', this.onTriggerClick,  this);
39912             this.el.removeClass('x-combo-noedit');
39913         }
39914     },
39915
39916     // private
39917     onBeforeLoad : function(){
39918         if(!this.hasFocus){
39919             return;
39920         }
39921         this.innerList.update(this.loadingText ?
39922                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39923         this.restrictHeight();
39924         this.selectedIndex = -1;
39925     },
39926
39927     // private
39928     onLoad : function(){
39929         if(!this.hasFocus){
39930             return;
39931         }
39932         if(this.store.getCount() > 0){
39933             this.expand();
39934             this.restrictHeight();
39935             if(this.lastQuery == this.allQuery){
39936                 if(this.editable){
39937                     this.el.dom.select();
39938                 }
39939                 if(!this.selectByValue(this.value, true)){
39940                     this.select(0, true);
39941                 }
39942             }else{
39943                 this.selectNext();
39944                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39945                     this.taTask.delay(this.typeAheadDelay);
39946                 }
39947             }
39948         }else{
39949             this.onEmptyResults();
39950         }
39951         //this.el.focus();
39952     },
39953     // private
39954     onLoadException : function()
39955     {
39956         this.collapse();
39957         Roo.log(this.store.reader.jsonData);
39958         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39959             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39960         }
39961         
39962         
39963     },
39964     // private
39965     onTypeAhead : function(){
39966         if(this.store.getCount() > 0){
39967             var r = this.store.getAt(0);
39968             var newValue = r.data[this.displayField];
39969             var len = newValue.length;
39970             var selStart = this.getRawValue().length;
39971             if(selStart != len){
39972                 this.setRawValue(newValue);
39973                 this.selectText(selStart, newValue.length);
39974             }
39975         }
39976     },
39977
39978     // private
39979     onSelect : function(record, index){
39980         if(this.fireEvent('beforeselect', this, record, index) !== false){
39981             this.setFromData(index > -1 ? record.data : false);
39982             this.collapse();
39983             this.fireEvent('select', this, record, index);
39984         }
39985     },
39986
39987     /**
39988      * Returns the currently selected field value or empty string if no value is set.
39989      * @return {String} value The selected value
39990      */
39991     getValue : function(){
39992         if(this.valueField){
39993             return typeof this.value != 'undefined' ? this.value : '';
39994         }
39995         return Roo.form.ComboBox.superclass.getValue.call(this);
39996     },
39997
39998     /**
39999      * Clears any text/value currently set in the field
40000      */
40001     clearValue : function(){
40002         if(this.hiddenField){
40003             this.hiddenField.value = '';
40004         }
40005         this.value = '';
40006         this.setRawValue('');
40007         this.lastSelectionText = '';
40008         
40009     },
40010
40011     /**
40012      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
40013      * will be displayed in the field.  If the value does not match the data value of an existing item,
40014      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
40015      * Otherwise the field will be blank (although the value will still be set).
40016      * @param {String} value The value to match
40017      */
40018     setValue : function(v){
40019         var text = v;
40020         if(this.valueField){
40021             var r = this.findRecord(this.valueField, v);
40022             if(r){
40023                 text = r.data[this.displayField];
40024             }else if(this.valueNotFoundText !== undefined){
40025                 text = this.valueNotFoundText;
40026             }
40027         }
40028         this.lastSelectionText = text;
40029         if(this.hiddenField){
40030             this.hiddenField.value = v;
40031         }
40032         Roo.form.ComboBox.superclass.setValue.call(this, text);
40033         this.value = v;
40034     },
40035     /**
40036      * @property {Object} the last set data for the element
40037      */
40038     
40039     lastData : false,
40040     /**
40041      * Sets the value of the field based on a object which is related to the record format for the store.
40042      * @param {Object} value the value to set as. or false on reset?
40043      */
40044     setFromData : function(o){
40045         var dv = ''; // display value
40046         var vv = ''; // value value..
40047         this.lastData = o;
40048         if (this.displayField) {
40049             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
40050         } else {
40051             // this is an error condition!!!
40052             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40053         }
40054         
40055         if(this.valueField){
40056             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40057         }
40058         if(this.hiddenField){
40059             this.hiddenField.value = vv;
40060             
40061             this.lastSelectionText = dv;
40062             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40063             this.value = vv;
40064             return;
40065         }
40066         // no hidden field.. - we store the value in 'value', but still display
40067         // display field!!!!
40068         this.lastSelectionText = dv;
40069         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40070         this.value = vv;
40071         
40072         
40073     },
40074     // private
40075     reset : function(){
40076         // overridden so that last data is reset..
40077         this.setValue(this.resetValue);
40078         this.clearInvalid();
40079         this.lastData = false;
40080         if (this.view) {
40081             this.view.clearSelections();
40082         }
40083     },
40084     // private
40085     findRecord : function(prop, value){
40086         var record;
40087         if(this.store.getCount() > 0){
40088             this.store.each(function(r){
40089                 if(r.data[prop] == value){
40090                     record = r;
40091                     return false;
40092                 }
40093                 return true;
40094             });
40095         }
40096         return record;
40097     },
40098     
40099     getName: function()
40100     {
40101         // returns hidden if it's set..
40102         if (!this.rendered) {return ''};
40103         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40104         
40105     },
40106     // private
40107     onViewMove : function(e, t){
40108         this.inKeyMode = false;
40109     },
40110
40111     // private
40112     onViewOver : function(e, t){
40113         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40114             return;
40115         }
40116         var item = this.view.findItemFromChild(t);
40117         if(item){
40118             var index = this.view.indexOf(item);
40119             this.select(index, false);
40120         }
40121     },
40122
40123     // private
40124     onViewClick : function(doFocus)
40125     {
40126         var index = this.view.getSelectedIndexes()[0];
40127         var r = this.store.getAt(index);
40128         if(r){
40129             this.onSelect(r, index);
40130         }
40131         if(doFocus !== false && !this.blockFocus){
40132             this.el.focus();
40133         }
40134     },
40135
40136     // private
40137     restrictHeight : function(){
40138         this.innerList.dom.style.height = '';
40139         var inner = this.innerList.dom;
40140         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40141         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40142         this.list.beginUpdate();
40143         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40144         this.list.alignTo(this.el, this.listAlign);
40145         this.list.endUpdate();
40146     },
40147
40148     // private
40149     onEmptyResults : function(){
40150         this.collapse();
40151     },
40152
40153     /**
40154      * Returns true if the dropdown list is expanded, else false.
40155      */
40156     isExpanded : function(){
40157         return this.list.isVisible();
40158     },
40159
40160     /**
40161      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40162      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40163      * @param {String} value The data value of the item to select
40164      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40165      * selected item if it is not currently in view (defaults to true)
40166      * @return {Boolean} True if the value matched an item in the list, else false
40167      */
40168     selectByValue : function(v, scrollIntoView){
40169         if(v !== undefined && v !== null){
40170             var r = this.findRecord(this.valueField || this.displayField, v);
40171             if(r){
40172                 this.select(this.store.indexOf(r), scrollIntoView);
40173                 return true;
40174             }
40175         }
40176         return false;
40177     },
40178
40179     /**
40180      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40181      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40182      * @param {Number} index The zero-based index of the list item to select
40183      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40184      * selected item if it is not currently in view (defaults to true)
40185      */
40186     select : function(index, scrollIntoView){
40187         this.selectedIndex = index;
40188         this.view.select(index);
40189         if(scrollIntoView !== false){
40190             var el = this.view.getNode(index);
40191             if(el){
40192                 this.innerList.scrollChildIntoView(el, false);
40193             }
40194         }
40195     },
40196
40197     // private
40198     selectNext : function(){
40199         var ct = this.store.getCount();
40200         if(ct > 0){
40201             if(this.selectedIndex == -1){
40202                 this.select(0);
40203             }else if(this.selectedIndex < ct-1){
40204                 this.select(this.selectedIndex+1);
40205             }
40206         }
40207     },
40208
40209     // private
40210     selectPrev : function(){
40211         var ct = this.store.getCount();
40212         if(ct > 0){
40213             if(this.selectedIndex == -1){
40214                 this.select(0);
40215             }else if(this.selectedIndex != 0){
40216                 this.select(this.selectedIndex-1);
40217             }
40218         }
40219     },
40220
40221     // private
40222     onKeyUp : function(e){
40223         if(this.editable !== false && !e.isSpecialKey()){
40224             this.lastKey = e.getKey();
40225             this.dqTask.delay(this.queryDelay);
40226         }
40227     },
40228
40229     // private
40230     validateBlur : function(){
40231         return !this.list || !this.list.isVisible();   
40232     },
40233
40234     // private
40235     initQuery : function(){
40236         this.doQuery(this.getRawValue());
40237     },
40238
40239     // private
40240     doForce : function(){
40241         if(this.el.dom.value.length > 0){
40242             this.el.dom.value =
40243                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40244              
40245         }
40246     },
40247
40248     /**
40249      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40250      * query allowing the query action to be canceled if needed.
40251      * @param {String} query The SQL query to execute
40252      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40253      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40254      * saved in the current store (defaults to false)
40255      */
40256     doQuery : function(q, forceAll){
40257         if(q === undefined || q === null){
40258             q = '';
40259         }
40260         var qe = {
40261             query: q,
40262             forceAll: forceAll,
40263             combo: this,
40264             cancel:false
40265         };
40266         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40267             return false;
40268         }
40269         q = qe.query;
40270         forceAll = qe.forceAll;
40271         if(forceAll === true || (q.length >= this.minChars)){
40272             if(this.lastQuery != q || this.alwaysQuery){
40273                 this.lastQuery = q;
40274                 if(this.mode == 'local'){
40275                     this.selectedIndex = -1;
40276                     if(forceAll){
40277                         this.store.clearFilter();
40278                     }else{
40279                         this.store.filter(this.displayField, q);
40280                     }
40281                     this.onLoad();
40282                 }else{
40283                     this.store.baseParams[this.queryParam] = q;
40284                     this.store.load({
40285                         params: this.getParams(q)
40286                     });
40287                     this.expand();
40288                 }
40289             }else{
40290                 this.selectedIndex = -1;
40291                 this.onLoad();   
40292             }
40293         }
40294     },
40295
40296     // private
40297     getParams : function(q){
40298         var p = {};
40299         //p[this.queryParam] = q;
40300         if(this.pageSize){
40301             p.start = 0;
40302             p.limit = this.pageSize;
40303         }
40304         return p;
40305     },
40306
40307     /**
40308      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40309      */
40310     collapse : function(){
40311         if(!this.isExpanded()){
40312             return;
40313         }
40314         this.list.hide();
40315         Roo.get(document).un('mousedown', this.collapseIf, this);
40316         Roo.get(document).un('mousewheel', this.collapseIf, this);
40317         if (!this.editable) {
40318             Roo.get(document).un('keydown', this.listKeyPress, this);
40319         }
40320         this.fireEvent('collapse', this);
40321     },
40322
40323     // private
40324     collapseIf : function(e){
40325         if(!e.within(this.wrap) && !e.within(this.list)){
40326             this.collapse();
40327         }
40328     },
40329
40330     /**
40331      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40332      */
40333     expand : function(){
40334         if(this.isExpanded() || !this.hasFocus){
40335             return;
40336         }
40337         this.list.alignTo(this.el, this.listAlign);
40338         this.list.show();
40339         Roo.get(document).on('mousedown', this.collapseIf, this);
40340         Roo.get(document).on('mousewheel', this.collapseIf, this);
40341         if (!this.editable) {
40342             Roo.get(document).on('keydown', this.listKeyPress, this);
40343         }
40344         
40345         this.fireEvent('expand', this);
40346     },
40347
40348     // private
40349     // Implements the default empty TriggerField.onTriggerClick function
40350     onTriggerClick : function(){
40351         if(this.disabled){
40352             return;
40353         }
40354         if(this.isExpanded()){
40355             this.collapse();
40356             if (!this.blockFocus) {
40357                 this.el.focus();
40358             }
40359             
40360         }else {
40361             this.hasFocus = true;
40362             if(this.triggerAction == 'all') {
40363                 this.doQuery(this.allQuery, true);
40364             } else {
40365                 this.doQuery(this.getRawValue());
40366             }
40367             if (!this.blockFocus) {
40368                 this.el.focus();
40369             }
40370         }
40371     },
40372     listKeyPress : function(e)
40373     {
40374         //Roo.log('listkeypress');
40375         // scroll to first matching element based on key pres..
40376         if (e.isSpecialKey()) {
40377             return false;
40378         }
40379         var k = String.fromCharCode(e.getKey()).toUpperCase();
40380         //Roo.log(k);
40381         var match  = false;
40382         var csel = this.view.getSelectedNodes();
40383         var cselitem = false;
40384         if (csel.length) {
40385             var ix = this.view.indexOf(csel[0]);
40386             cselitem  = this.store.getAt(ix);
40387             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40388                 cselitem = false;
40389             }
40390             
40391         }
40392         
40393         this.store.each(function(v) { 
40394             if (cselitem) {
40395                 // start at existing selection.
40396                 if (cselitem.id == v.id) {
40397                     cselitem = false;
40398                 }
40399                 return;
40400             }
40401                 
40402             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40403                 match = this.store.indexOf(v);
40404                 return false;
40405             }
40406         }, this);
40407         
40408         if (match === false) {
40409             return true; // no more action?
40410         }
40411         // scroll to?
40412         this.view.select(match);
40413         var sn = Roo.get(this.view.getSelectedNodes()[0]);
40414         sn.scrollIntoView(sn.dom.parentNode, false);
40415     }
40416
40417     /** 
40418     * @cfg {Boolean} grow 
40419     * @hide 
40420     */
40421     /** 
40422     * @cfg {Number} growMin 
40423     * @hide 
40424     */
40425     /** 
40426     * @cfg {Number} growMax 
40427     * @hide 
40428     */
40429     /**
40430      * @hide
40431      * @method autoSize
40432      */
40433 });/*
40434  * Copyright(c) 2010-2012, Roo J Solutions Limited
40435  *
40436  * Licence LGPL
40437  *
40438  */
40439
40440 /**
40441  * @class Roo.form.ComboBoxArray
40442  * @extends Roo.form.TextField
40443  * A facebook style adder... for lists of email / people / countries  etc...
40444  * pick multiple items from a combo box, and shows each one.
40445  *
40446  *  Fred [x]  Brian [x]  [Pick another |v]
40447  *
40448  *
40449  *  For this to work: it needs various extra information
40450  *    - normal combo problay has
40451  *      name, hiddenName
40452  *    + displayField, valueField
40453  *
40454  *    For our purpose...
40455  *
40456  *
40457  *   If we change from 'extends' to wrapping...
40458  *   
40459  *  
40460  *
40461  
40462  
40463  * @constructor
40464  * Create a new ComboBoxArray.
40465  * @param {Object} config Configuration options
40466  */
40467  
40468
40469 Roo.form.ComboBoxArray = function(config)
40470 {
40471     this.addEvents({
40472         /**
40473          * @event beforeremove
40474          * Fires before remove the value from the list
40475              * @param {Roo.form.ComboBoxArray} _self This combo box array
40476              * @param {Roo.form.ComboBoxArray.Item} item removed item
40477              */
40478         'beforeremove' : true,
40479         /**
40480          * @event remove
40481          * Fires when remove the value from the list
40482              * @param {Roo.form.ComboBoxArray} _self This combo box array
40483              * @param {Roo.form.ComboBoxArray.Item} item removed item
40484              */
40485         'remove' : true
40486         
40487         
40488     });
40489     
40490     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40491     
40492     this.items = new Roo.util.MixedCollection(false);
40493     
40494     // construct the child combo...
40495     
40496     
40497     
40498     
40499    
40500     
40501 }
40502
40503  
40504 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40505
40506     /**
40507      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40508      */
40509     
40510     lastData : false,
40511     
40512     // behavies liek a hiddne field
40513     inputType:      'hidden',
40514     /**
40515      * @cfg {Number} width The width of the box that displays the selected element
40516      */ 
40517     width:          300,
40518
40519     
40520     
40521     /**
40522      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40523      */
40524     name : false,
40525     /**
40526      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40527      */
40528     hiddenName : false,
40529     
40530     
40531     // private the array of items that are displayed..
40532     items  : false,
40533     // private - the hidden field el.
40534     hiddenEl : false,
40535     // private - the filed el..
40536     el : false,
40537     
40538     //validateValue : function() { return true; }, // all values are ok!
40539     //onAddClick: function() { },
40540     
40541     onRender : function(ct, position) 
40542     {
40543         
40544         // create the standard hidden element
40545         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40546         
40547         
40548         // give fake names to child combo;
40549         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40550         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40551         
40552         this.combo = Roo.factory(this.combo, Roo.form);
40553         this.combo.onRender(ct, position);
40554         if (typeof(this.combo.width) != 'undefined') {
40555             this.combo.onResize(this.combo.width,0);
40556         }
40557         
40558         this.combo.initEvents();
40559         
40560         // assigned so form know we need to do this..
40561         this.store          = this.combo.store;
40562         this.valueField     = this.combo.valueField;
40563         this.displayField   = this.combo.displayField ;
40564         
40565         
40566         this.combo.wrap.addClass('x-cbarray-grp');
40567         
40568         var cbwrap = this.combo.wrap.createChild(
40569             {tag: 'div', cls: 'x-cbarray-cb'},
40570             this.combo.el.dom
40571         );
40572         
40573              
40574         this.hiddenEl = this.combo.wrap.createChild({
40575             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40576         });
40577         this.el = this.combo.wrap.createChild({
40578             tag: 'input',  type:'hidden' , name: this.name, value : ''
40579         });
40580          //   this.el.dom.removeAttribute("name");
40581         
40582         
40583         this.outerWrap = this.combo.wrap;
40584         this.wrap = cbwrap;
40585         
40586         this.outerWrap.setWidth(this.width);
40587         this.outerWrap.dom.removeChild(this.el.dom);
40588         
40589         this.wrap.dom.appendChild(this.el.dom);
40590         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40591         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40592         
40593         this.combo.trigger.setStyle('position','relative');
40594         this.combo.trigger.setStyle('left', '0px');
40595         this.combo.trigger.setStyle('top', '2px');
40596         
40597         this.combo.el.setStyle('vertical-align', 'text-bottom');
40598         
40599         //this.trigger.setStyle('vertical-align', 'top');
40600         
40601         // this should use the code from combo really... on('add' ....)
40602         if (this.adder) {
40603             
40604         
40605             this.adder = this.outerWrap.createChild(
40606                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40607             var _t = this;
40608             this.adder.on('click', function(e) {
40609                 _t.fireEvent('adderclick', this, e);
40610             }, _t);
40611         }
40612         //var _t = this;
40613         //this.adder.on('click', this.onAddClick, _t);
40614         
40615         
40616         this.combo.on('select', function(cb, rec, ix) {
40617             this.addItem(rec.data);
40618             
40619             cb.setValue('');
40620             cb.el.dom.value = '';
40621             //cb.lastData = rec.data;
40622             // add to list
40623             
40624         }, this);
40625         
40626         
40627     },
40628     
40629     
40630     getName: function()
40631     {
40632         // returns hidden if it's set..
40633         if (!this.rendered) {return ''};
40634         return  this.hiddenName ? this.hiddenName : this.name;
40635         
40636     },
40637     
40638     
40639     onResize: function(w, h){
40640         
40641         return;
40642         // not sure if this is needed..
40643         //this.combo.onResize(w,h);
40644         
40645         if(typeof w != 'number'){
40646             // we do not handle it!?!?
40647             return;
40648         }
40649         var tw = this.combo.trigger.getWidth();
40650         tw += this.addicon ? this.addicon.getWidth() : 0;
40651         tw += this.editicon ? this.editicon.getWidth() : 0;
40652         var x = w - tw;
40653         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40654             
40655         this.combo.trigger.setStyle('left', '0px');
40656         
40657         if(this.list && this.listWidth === undefined){
40658             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40659             this.list.setWidth(lw);
40660             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40661         }
40662         
40663     
40664         
40665     },
40666     
40667     addItem: function(rec)
40668     {
40669         var valueField = this.combo.valueField;
40670         var displayField = this.combo.displayField;
40671         if (this.items.indexOfKey(rec[valueField]) > -1) {
40672             //console.log("GOT " + rec.data.id);
40673             return;
40674         }
40675         
40676         var x = new Roo.form.ComboBoxArray.Item({
40677             //id : rec[this.idField],
40678             data : rec,
40679             displayField : displayField ,
40680             tipField : displayField ,
40681             cb : this
40682         });
40683         // use the 
40684         this.items.add(rec[valueField],x);
40685         // add it before the element..
40686         this.updateHiddenEl();
40687         x.render(this.outerWrap, this.wrap.dom);
40688         // add the image handler..
40689     },
40690     
40691     updateHiddenEl : function()
40692     {
40693         this.validate();
40694         if (!this.hiddenEl) {
40695             return;
40696         }
40697         var ar = [];
40698         var idField = this.combo.valueField;
40699         
40700         this.items.each(function(f) {
40701             ar.push(f.data[idField]);
40702            
40703         });
40704         this.hiddenEl.dom.value = ar.join(',');
40705         this.validate();
40706     },
40707     
40708     reset : function()
40709     {
40710         this.items.clear();
40711         
40712         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
40713            el.remove();
40714         });
40715         
40716         this.el.dom.value = '';
40717         if (this.hiddenEl) {
40718             this.hiddenEl.dom.value = '';
40719         }
40720         
40721     },
40722     getValue: function()
40723     {
40724         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40725     },
40726     setValue: function(v) // not a valid action - must use addItems..
40727     {
40728          
40729         this.reset();
40730         
40731         
40732         
40733         if (this.store.isLocal && (typeof(v) == 'string')) {
40734             // then we can use the store to find the values..
40735             // comma seperated at present.. this needs to allow JSON based encoding..
40736             this.hiddenEl.value  = v;
40737             var v_ar = [];
40738             Roo.each(v.split(','), function(k) {
40739                 Roo.log("CHECK " + this.valueField + ',' + k);
40740                 var li = this.store.query(this.valueField, k);
40741                 if (!li.length) {
40742                     return;
40743                 }
40744                 var add = {};
40745                 add[this.valueField] = k;
40746                 add[this.displayField] = li.item(0).data[this.displayField];
40747                 
40748                 this.addItem(add);
40749             }, this) 
40750              
40751         }
40752         if (typeof(v) == 'object' ) {
40753             // then let's assume it's an array of objects..
40754             Roo.each(v, function(l) {
40755                 this.addItem(l);
40756             }, this);
40757              
40758         }
40759         
40760         
40761     },
40762     setFromData: function(v)
40763     {
40764         // this recieves an object, if setValues is called.
40765         this.reset();
40766         this.el.dom.value = v[this.displayField];
40767         this.hiddenEl.dom.value = v[this.valueField];
40768         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40769             return;
40770         }
40771         var kv = v[this.valueField];
40772         var dv = v[this.displayField];
40773         kv = typeof(kv) != 'string' ? '' : kv;
40774         dv = typeof(dv) != 'string' ? '' : dv;
40775         
40776         
40777         var keys = kv.split(',');
40778         var display = dv.split(',');
40779         for (var i = 0 ; i < keys.length; i++) {
40780             
40781             add = {};
40782             add[this.valueField] = keys[i];
40783             add[this.displayField] = display[i];
40784             this.addItem(add);
40785         }
40786       
40787         
40788     },
40789     
40790     /**
40791      * Validates the combox array value
40792      * @return {Boolean} True if the value is valid, else false
40793      */
40794     validate : function(){
40795         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40796             this.clearInvalid();
40797             return true;
40798         }
40799         return false;
40800     },
40801     
40802     validateValue : function(value){
40803         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40804         
40805     },
40806     
40807     /*@
40808      * overide
40809      * 
40810      */
40811     isDirty : function() {
40812         if(this.disabled) {
40813             return false;
40814         }
40815         
40816         try {
40817             var d = Roo.decode(String(this.originalValue));
40818         } catch (e) {
40819             return String(this.getValue()) !== String(this.originalValue);
40820         }
40821         
40822         var originalValue = [];
40823         
40824         for (var i = 0; i < d.length; i++){
40825             originalValue.push(d[i][this.valueField]);
40826         }
40827         
40828         return String(this.getValue()) !== String(originalValue.join(','));
40829         
40830     }
40831     
40832 });
40833
40834
40835
40836 /**
40837  * @class Roo.form.ComboBoxArray.Item
40838  * @extends Roo.BoxComponent
40839  * A selected item in the list
40840  *  Fred [x]  Brian [x]  [Pick another |v]
40841  * 
40842  * @constructor
40843  * Create a new item.
40844  * @param {Object} config Configuration options
40845  */
40846  
40847 Roo.form.ComboBoxArray.Item = function(config) {
40848     config.id = Roo.id();
40849     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40850 }
40851
40852 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40853     data : {},
40854     cb: false,
40855     displayField : false,
40856     tipField : false,
40857     
40858     
40859     defaultAutoCreate : {
40860         tag: 'div',
40861         cls: 'x-cbarray-item',
40862         cn : [ 
40863             { tag: 'div' },
40864             {
40865                 tag: 'img',
40866                 width:16,
40867                 height : 16,
40868                 src : Roo.BLANK_IMAGE_URL ,
40869                 align: 'center'
40870             }
40871         ]
40872         
40873     },
40874     
40875  
40876     onRender : function(ct, position)
40877     {
40878         Roo.form.Field.superclass.onRender.call(this, ct, position);
40879         
40880         if(!this.el){
40881             var cfg = this.getAutoCreate();
40882             this.el = ct.createChild(cfg, position);
40883         }
40884         
40885         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40886         
40887         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40888             this.cb.renderer(this.data) :
40889             String.format('{0}',this.data[this.displayField]);
40890         
40891             
40892         this.el.child('div').dom.setAttribute('qtip',
40893                         String.format('{0}',this.data[this.tipField])
40894         );
40895         
40896         this.el.child('img').on('click', this.remove, this);
40897         
40898     },
40899    
40900     remove : function()
40901     {
40902         if(this.cb.disabled){
40903             return;
40904         }
40905         
40906         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40907             this.cb.items.remove(this);
40908             this.el.child('img').un('click', this.remove, this);
40909             this.el.remove();
40910             this.cb.updateHiddenEl();
40911
40912             this.cb.fireEvent('remove', this.cb, this);
40913         }
40914         
40915     }
40916 });/*
40917  * Based on:
40918  * Ext JS Library 1.1.1
40919  * Copyright(c) 2006-2007, Ext JS, LLC.
40920  *
40921  * Originally Released Under LGPL - original licence link has changed is not relivant.
40922  *
40923  * Fork - LGPL
40924  * <script type="text/javascript">
40925  */
40926 /**
40927  * @class Roo.form.Checkbox
40928  * @extends Roo.form.Field
40929  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40930  * @constructor
40931  * Creates a new Checkbox
40932  * @param {Object} config Configuration options
40933  */
40934 Roo.form.Checkbox = function(config){
40935     Roo.form.Checkbox.superclass.constructor.call(this, config);
40936     this.addEvents({
40937         /**
40938          * @event check
40939          * Fires when the checkbox is checked or unchecked.
40940              * @param {Roo.form.Checkbox} this This checkbox
40941              * @param {Boolean} checked The new checked value
40942              */
40943         check : true
40944     });
40945 };
40946
40947 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40948     /**
40949      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40950      */
40951     focusClass : undefined,
40952     /**
40953      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40954      */
40955     fieldClass: "x-form-field",
40956     /**
40957      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40958      */
40959     checked: false,
40960     /**
40961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40962      * {tag: "input", type: "checkbox", autocomplete: "off"})
40963      */
40964     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40965     /**
40966      * @cfg {String} boxLabel The text that appears beside the checkbox
40967      */
40968     boxLabel : "",
40969     /**
40970      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40971      */  
40972     inputValue : '1',
40973     /**
40974      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40975      */
40976      valueOff: '0', // value when not checked..
40977
40978     actionMode : 'viewEl', 
40979     //
40980     // private
40981     itemCls : 'x-menu-check-item x-form-item',
40982     groupClass : 'x-menu-group-item',
40983     inputType : 'hidden',
40984     
40985     
40986     inSetChecked: false, // check that we are not calling self...
40987     
40988     inputElement: false, // real input element?
40989     basedOn: false, // ????
40990     
40991     isFormField: true, // not sure where this is needed!!!!
40992
40993     onResize : function(){
40994         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40995         if(!this.boxLabel){
40996             this.el.alignTo(this.wrap, 'c-c');
40997         }
40998     },
40999
41000     initEvents : function(){
41001         Roo.form.Checkbox.superclass.initEvents.call(this);
41002         this.el.on("click", this.onClick,  this);
41003         this.el.on("change", this.onClick,  this);
41004     },
41005
41006
41007     getResizeEl : function(){
41008         return this.wrap;
41009     },
41010
41011     getPositionEl : function(){
41012         return this.wrap;
41013     },
41014
41015     // private
41016     onRender : function(ct, position){
41017         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41018         /*
41019         if(this.inputValue !== undefined){
41020             this.el.dom.value = this.inputValue;
41021         }
41022         */
41023         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41024         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41025         var viewEl = this.wrap.createChild({ 
41026             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41027         this.viewEl = viewEl;   
41028         this.wrap.on('click', this.onClick,  this); 
41029         
41030         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41031         this.el.on('propertychange', this.setFromHidden,  this);  //ie
41032         
41033         
41034         
41035         if(this.boxLabel){
41036             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41037         //    viewEl.on('click', this.onClick,  this); 
41038         }
41039         //if(this.checked){
41040             this.setChecked(this.checked);
41041         //}else{
41042             //this.checked = this.el.dom;
41043         //}
41044
41045     },
41046
41047     // private
41048     initValue : Roo.emptyFn,
41049
41050     /**
41051      * Returns the checked state of the checkbox.
41052      * @return {Boolean} True if checked, else false
41053      */
41054     getValue : function(){
41055         if(this.el){
41056             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
41057         }
41058         return this.valueOff;
41059         
41060     },
41061
41062         // private
41063     onClick : function(){ 
41064         if (this.disabled) {
41065             return;
41066         }
41067         this.setChecked(!this.checked);
41068
41069         //if(this.el.dom.checked != this.checked){
41070         //    this.setValue(this.el.dom.checked);
41071        // }
41072     },
41073
41074     /**
41075      * Sets the checked state of the checkbox.
41076      * On is always based on a string comparison between inputValue and the param.
41077      * @param {Boolean/String} value - the value to set 
41078      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41079      */
41080     setValue : function(v,suppressEvent){
41081         
41082         
41083         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41084         //if(this.el && this.el.dom){
41085         //    this.el.dom.checked = this.checked;
41086         //    this.el.dom.defaultChecked = this.checked;
41087         //}
41088         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41089         //this.fireEvent("check", this, this.checked);
41090     },
41091     // private..
41092     setChecked : function(state,suppressEvent)
41093     {
41094         if (this.inSetChecked) {
41095             this.checked = state;
41096             return;
41097         }
41098         
41099     
41100         if(this.wrap){
41101             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41102         }
41103         this.checked = state;
41104         if(suppressEvent !== true){
41105             this.fireEvent('check', this, state);
41106         }
41107         this.inSetChecked = true;
41108         this.el.dom.value = state ? this.inputValue : this.valueOff;
41109         this.inSetChecked = false;
41110         
41111     },
41112     // handle setting of hidden value by some other method!!?!?
41113     setFromHidden: function()
41114     {
41115         if(!this.el){
41116             return;
41117         }
41118         //console.log("SET FROM HIDDEN");
41119         //alert('setFrom hidden');
41120         this.setValue(this.el.dom.value);
41121     },
41122     
41123     onDestroy : function()
41124     {
41125         if(this.viewEl){
41126             Roo.get(this.viewEl).remove();
41127         }
41128          
41129         Roo.form.Checkbox.superclass.onDestroy.call(this);
41130     },
41131     
41132     setBoxLabel : function(str)
41133     {
41134         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
41135     }
41136
41137 });/*
41138  * Based on:
41139  * Ext JS Library 1.1.1
41140  * Copyright(c) 2006-2007, Ext JS, LLC.
41141  *
41142  * Originally Released Under LGPL - original licence link has changed is not relivant.
41143  *
41144  * Fork - LGPL
41145  * <script type="text/javascript">
41146  */
41147  
41148 /**
41149  * @class Roo.form.Radio
41150  * @extends Roo.form.Checkbox
41151  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41152  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41153  * @constructor
41154  * Creates a new Radio
41155  * @param {Object} config Configuration options
41156  */
41157 Roo.form.Radio = function(){
41158     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41159 };
41160 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41161     inputType: 'radio',
41162
41163     /**
41164      * If this radio is part of a group, it will return the selected value
41165      * @return {String}
41166      */
41167     getGroupValue : function(){
41168         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41169     },
41170     
41171     
41172     onRender : function(ct, position){
41173         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41174         
41175         if(this.inputValue !== undefined){
41176             this.el.dom.value = this.inputValue;
41177         }
41178          
41179         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41180         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41181         //var viewEl = this.wrap.createChild({ 
41182         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41183         //this.viewEl = viewEl;   
41184         //this.wrap.on('click', this.onClick,  this); 
41185         
41186         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41187         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41188         
41189         
41190         
41191         if(this.boxLabel){
41192             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41193         //    viewEl.on('click', this.onClick,  this); 
41194         }
41195          if(this.checked){
41196             this.el.dom.checked =   'checked' ;
41197         }
41198          
41199     } 
41200     
41201     
41202 });//<script type="text/javascript">
41203
41204 /*
41205  * Based  Ext JS Library 1.1.1
41206  * Copyright(c) 2006-2007, Ext JS, LLC.
41207  * LGPL
41208  *
41209  */
41210  
41211 /**
41212  * @class Roo.HtmlEditorCore
41213  * @extends Roo.Component
41214  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41215  *
41216  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41217  */
41218
41219 Roo.HtmlEditorCore = function(config){
41220     
41221     
41222     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41223     
41224     
41225     this.addEvents({
41226         /**
41227          * @event initialize
41228          * Fires when the editor is fully initialized (including the iframe)
41229          * @param {Roo.HtmlEditorCore} this
41230          */
41231         initialize: true,
41232         /**
41233          * @event activate
41234          * Fires when the editor is first receives the focus. Any insertion must wait
41235          * until after this event.
41236          * @param {Roo.HtmlEditorCore} this
41237          */
41238         activate: true,
41239          /**
41240          * @event beforesync
41241          * Fires before the textarea is updated with content from the editor iframe. Return false
41242          * to cancel the sync.
41243          * @param {Roo.HtmlEditorCore} this
41244          * @param {String} html
41245          */
41246         beforesync: true,
41247          /**
41248          * @event beforepush
41249          * Fires before the iframe editor is updated with content from the textarea. Return false
41250          * to cancel the push.
41251          * @param {Roo.HtmlEditorCore} this
41252          * @param {String} html
41253          */
41254         beforepush: true,
41255          /**
41256          * @event sync
41257          * Fires when the textarea is updated with content from the editor iframe.
41258          * @param {Roo.HtmlEditorCore} this
41259          * @param {String} html
41260          */
41261         sync: true,
41262          /**
41263          * @event push
41264          * Fires when the iframe editor is updated with content from the textarea.
41265          * @param {Roo.HtmlEditorCore} this
41266          * @param {String} html
41267          */
41268         push: true,
41269         
41270         /**
41271          * @event editorevent
41272          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41273          * @param {Roo.HtmlEditorCore} this
41274          */
41275         editorevent: true
41276         
41277     });
41278     
41279     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41280     
41281     // defaults : white / black...
41282     this.applyBlacklists();
41283     
41284     
41285     
41286 };
41287
41288
41289 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41290
41291
41292      /**
41293      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41294      */
41295     
41296     owner : false,
41297     
41298      /**
41299      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41300      *                        Roo.resizable.
41301      */
41302     resizable : false,
41303      /**
41304      * @cfg {Number} height (in pixels)
41305      */   
41306     height: 300,
41307    /**
41308      * @cfg {Number} width (in pixels)
41309      */   
41310     width: 500,
41311     
41312     /**
41313      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41314      * 
41315      */
41316     stylesheets: false,
41317     
41318     // id of frame..
41319     frameId: false,
41320     
41321     // private properties
41322     validationEvent : false,
41323     deferHeight: true,
41324     initialized : false,
41325     activated : false,
41326     sourceEditMode : false,
41327     onFocus : Roo.emptyFn,
41328     iframePad:3,
41329     hideMode:'offsets',
41330     
41331     clearUp: true,
41332     
41333     // blacklist + whitelisted elements..
41334     black: false,
41335     white: false,
41336      
41337     
41338
41339     /**
41340      * Protected method that will not generally be called directly. It
41341      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41342      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41343      */
41344     getDocMarkup : function(){
41345         // body styles..
41346         var st = '';
41347         
41348         // inherit styels from page...?? 
41349         if (this.stylesheets === false) {
41350             
41351             Roo.get(document.head).select('style').each(function(node) {
41352                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41353             });
41354             
41355             Roo.get(document.head).select('link').each(function(node) { 
41356                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41357             });
41358             
41359         } else if (!this.stylesheets.length) {
41360                 // simple..
41361                 st = '<style type="text/css">' +
41362                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41363                    '</style>';
41364         } else { 
41365             
41366         }
41367         
41368         st +=  '<style type="text/css">' +
41369             'IMG { cursor: pointer } ' +
41370         '</style>';
41371
41372         
41373         return '<html><head>' + st  +
41374             //<style type="text/css">' +
41375             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41376             //'</style>' +
41377             ' </head><body class="roo-htmleditor-body"></body></html>';
41378     },
41379
41380     // private
41381     onRender : function(ct, position)
41382     {
41383         var _t = this;
41384         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41385         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41386         
41387         
41388         this.el.dom.style.border = '0 none';
41389         this.el.dom.setAttribute('tabIndex', -1);
41390         this.el.addClass('x-hidden hide');
41391         
41392         
41393         
41394         if(Roo.isIE){ // fix IE 1px bogus margin
41395             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41396         }
41397        
41398         
41399         this.frameId = Roo.id();
41400         
41401          
41402         
41403         var iframe = this.owner.wrap.createChild({
41404             tag: 'iframe',
41405             cls: 'form-control', // bootstrap..
41406             id: this.frameId,
41407             name: this.frameId,
41408             frameBorder : 'no',
41409             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41410         }, this.el
41411         );
41412         
41413         
41414         this.iframe = iframe.dom;
41415
41416          this.assignDocWin();
41417         
41418         this.doc.designMode = 'on';
41419        
41420         this.doc.open();
41421         this.doc.write(this.getDocMarkup());
41422         this.doc.close();
41423
41424         
41425         var task = { // must defer to wait for browser to be ready
41426             run : function(){
41427                 //console.log("run task?" + this.doc.readyState);
41428                 this.assignDocWin();
41429                 if(this.doc.body || this.doc.readyState == 'complete'){
41430                     try {
41431                         this.doc.designMode="on";
41432                     } catch (e) {
41433                         return;
41434                     }
41435                     Roo.TaskMgr.stop(task);
41436                     this.initEditor.defer(10, this);
41437                 }
41438             },
41439             interval : 10,
41440             duration: 10000,
41441             scope: this
41442         };
41443         Roo.TaskMgr.start(task);
41444
41445     },
41446
41447     // private
41448     onResize : function(w, h)
41449     {
41450          Roo.log('resize: ' +w + ',' + h );
41451         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41452         if(!this.iframe){
41453             return;
41454         }
41455         if(typeof w == 'number'){
41456             
41457             this.iframe.style.width = w + 'px';
41458         }
41459         if(typeof h == 'number'){
41460             
41461             this.iframe.style.height = h + 'px';
41462             if(this.doc){
41463                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41464             }
41465         }
41466         
41467     },
41468
41469     /**
41470      * Toggles the editor between standard and source edit mode.
41471      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41472      */
41473     toggleSourceEdit : function(sourceEditMode){
41474         
41475         this.sourceEditMode = sourceEditMode === true;
41476         
41477         if(this.sourceEditMode){
41478  
41479             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41480             
41481         }else{
41482             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41483             //this.iframe.className = '';
41484             this.deferFocus();
41485         }
41486         //this.setSize(this.owner.wrap.getSize());
41487         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41488     },
41489
41490     
41491   
41492
41493     /**
41494      * Protected method that will not generally be called directly. If you need/want
41495      * custom HTML cleanup, this is the method you should override.
41496      * @param {String} html The HTML to be cleaned
41497      * return {String} The cleaned HTML
41498      */
41499     cleanHtml : function(html){
41500         html = String(html);
41501         if(html.length > 5){
41502             if(Roo.isSafari){ // strip safari nonsense
41503                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41504             }
41505         }
41506         if(html == '&nbsp;'){
41507             html = '';
41508         }
41509         return html;
41510     },
41511
41512     /**
41513      * HTML Editor -> Textarea
41514      * Protected method that will not generally be called directly. Syncs the contents
41515      * of the editor iframe with the textarea.
41516      */
41517     syncValue : function(){
41518         if(this.initialized){
41519             var bd = (this.doc.body || this.doc.documentElement);
41520             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41521             var html = bd.innerHTML;
41522             if(Roo.isSafari){
41523                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41524                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41525                 if(m && m[1]){
41526                     html = '<div style="'+m[0]+'">' + html + '</div>';
41527                 }
41528             }
41529             html = this.cleanHtml(html);
41530             // fix up the special chars.. normaly like back quotes in word...
41531             // however we do not want to do this with chinese..
41532             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41533                 var cc = b.charCodeAt();
41534                 if (
41535                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41536                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41537                     (cc >= 0xf900 && cc < 0xfb00 )
41538                 ) {
41539                         return b;
41540                 }
41541                 return "&#"+cc+";" 
41542             });
41543             if(this.owner.fireEvent('beforesync', this, html) !== false){
41544                 this.el.dom.value = html;
41545                 this.owner.fireEvent('sync', this, html);
41546             }
41547         }
41548     },
41549
41550     /**
41551      * Protected method that will not generally be called directly. Pushes the value of the textarea
41552      * into the iframe editor.
41553      */
41554     pushValue : function(){
41555         if(this.initialized){
41556             var v = this.el.dom.value.trim();
41557             
41558 //            if(v.length < 1){
41559 //                v = '&#160;';
41560 //            }
41561             
41562             if(this.owner.fireEvent('beforepush', this, v) !== false){
41563                 var d = (this.doc.body || this.doc.documentElement);
41564                 d.innerHTML = v;
41565                 this.cleanUpPaste();
41566                 this.el.dom.value = d.innerHTML;
41567                 this.owner.fireEvent('push', this, v);
41568             }
41569         }
41570     },
41571
41572     // private
41573     deferFocus : function(){
41574         this.focus.defer(10, this);
41575     },
41576
41577     // doc'ed in Field
41578     focus : function(){
41579         if(this.win && !this.sourceEditMode){
41580             this.win.focus();
41581         }else{
41582             this.el.focus();
41583         }
41584     },
41585     
41586     assignDocWin: function()
41587     {
41588         var iframe = this.iframe;
41589         
41590          if(Roo.isIE){
41591             this.doc = iframe.contentWindow.document;
41592             this.win = iframe.contentWindow;
41593         } else {
41594 //            if (!Roo.get(this.frameId)) {
41595 //                return;
41596 //            }
41597 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41598 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41599             
41600             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41601                 return;
41602             }
41603             
41604             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41605             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41606         }
41607     },
41608     
41609     // private
41610     initEditor : function(){
41611         //console.log("INIT EDITOR");
41612         this.assignDocWin();
41613         
41614         
41615         
41616         this.doc.designMode="on";
41617         this.doc.open();
41618         this.doc.write(this.getDocMarkup());
41619         this.doc.close();
41620         
41621         var dbody = (this.doc.body || this.doc.documentElement);
41622         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41623         // this copies styles from the containing element into thsi one..
41624         // not sure why we need all of this..
41625         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41626         
41627         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41628         //ss['background-attachment'] = 'fixed'; // w3c
41629         dbody.bgProperties = 'fixed'; // ie
41630         //Roo.DomHelper.applyStyles(dbody, ss);
41631         Roo.EventManager.on(this.doc, {
41632             //'mousedown': this.onEditorEvent,
41633             'mouseup': this.onEditorEvent,
41634             'dblclick': this.onEditorEvent,
41635             'click': this.onEditorEvent,
41636             'keyup': this.onEditorEvent,
41637             buffer:100,
41638             scope: this
41639         });
41640         if(Roo.isGecko){
41641             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41642         }
41643         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41644             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41645         }
41646         this.initialized = true;
41647
41648         this.owner.fireEvent('initialize', this);
41649         this.pushValue();
41650     },
41651
41652     // private
41653     onDestroy : function(){
41654         
41655         
41656         
41657         if(this.rendered){
41658             
41659             //for (var i =0; i < this.toolbars.length;i++) {
41660             //    // fixme - ask toolbars for heights?
41661             //    this.toolbars[i].onDestroy();
41662            // }
41663             
41664             //this.wrap.dom.innerHTML = '';
41665             //this.wrap.remove();
41666         }
41667     },
41668
41669     // private
41670     onFirstFocus : function(){
41671         
41672         this.assignDocWin();
41673         
41674         
41675         this.activated = true;
41676          
41677     
41678         if(Roo.isGecko){ // prevent silly gecko errors
41679             this.win.focus();
41680             var s = this.win.getSelection();
41681             if(!s.focusNode || s.focusNode.nodeType != 3){
41682                 var r = s.getRangeAt(0);
41683                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41684                 r.collapse(true);
41685                 this.deferFocus();
41686             }
41687             try{
41688                 this.execCmd('useCSS', true);
41689                 this.execCmd('styleWithCSS', false);
41690             }catch(e){}
41691         }
41692         this.owner.fireEvent('activate', this);
41693     },
41694
41695     // private
41696     adjustFont: function(btn){
41697         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41698         //if(Roo.isSafari){ // safari
41699         //    adjust *= 2;
41700        // }
41701         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41702         if(Roo.isSafari){ // safari
41703             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41704             v =  (v < 10) ? 10 : v;
41705             v =  (v > 48) ? 48 : v;
41706             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41707             
41708         }
41709         
41710         
41711         v = Math.max(1, v+adjust);
41712         
41713         this.execCmd('FontSize', v  );
41714     },
41715
41716     onEditorEvent : function(e)
41717     {
41718         this.owner.fireEvent('editorevent', this, e);
41719       //  this.updateToolbar();
41720         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41721     },
41722
41723     insertTag : function(tg)
41724     {
41725         // could be a bit smarter... -> wrap the current selected tRoo..
41726         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41727             
41728             range = this.createRange(this.getSelection());
41729             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41730             wrappingNode.appendChild(range.extractContents());
41731             range.insertNode(wrappingNode);
41732
41733             return;
41734             
41735             
41736             
41737         }
41738         this.execCmd("formatblock",   tg);
41739         
41740     },
41741     
41742     insertText : function(txt)
41743     {
41744         
41745         
41746         var range = this.createRange();
41747         range.deleteContents();
41748                //alert(Sender.getAttribute('label'));
41749                
41750         range.insertNode(this.doc.createTextNode(txt));
41751     } ,
41752     
41753      
41754
41755     /**
41756      * Executes a Midas editor command on the editor document and performs necessary focus and
41757      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41758      * @param {String} cmd The Midas command
41759      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41760      */
41761     relayCmd : function(cmd, value){
41762         this.win.focus();
41763         this.execCmd(cmd, value);
41764         this.owner.fireEvent('editorevent', this);
41765         //this.updateToolbar();
41766         this.owner.deferFocus();
41767     },
41768
41769     /**
41770      * Executes a Midas editor command directly on the editor document.
41771      * For visual commands, you should use {@link #relayCmd} instead.
41772      * <b>This should only be called after the editor is initialized.</b>
41773      * @param {String} cmd The Midas command
41774      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41775      */
41776     execCmd : function(cmd, value){
41777         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41778         this.syncValue();
41779     },
41780  
41781  
41782    
41783     /**
41784      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41785      * to insert tRoo.
41786      * @param {String} text | dom node.. 
41787      */
41788     insertAtCursor : function(text)
41789     {
41790         
41791         
41792         
41793         if(!this.activated){
41794             return;
41795         }
41796         /*
41797         if(Roo.isIE){
41798             this.win.focus();
41799             var r = this.doc.selection.createRange();
41800             if(r){
41801                 r.collapse(true);
41802                 r.pasteHTML(text);
41803                 this.syncValue();
41804                 this.deferFocus();
41805             
41806             }
41807             return;
41808         }
41809         */
41810         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41811             this.win.focus();
41812             
41813             
41814             // from jquery ui (MIT licenced)
41815             var range, node;
41816             var win = this.win;
41817             
41818             if (win.getSelection && win.getSelection().getRangeAt) {
41819                 range = win.getSelection().getRangeAt(0);
41820                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41821                 range.insertNode(node);
41822             } else if (win.document.selection && win.document.selection.createRange) {
41823                 // no firefox support
41824                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41825                 win.document.selection.createRange().pasteHTML(txt);
41826             } else {
41827                 // no firefox support
41828                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41829                 this.execCmd('InsertHTML', txt);
41830             } 
41831             
41832             this.syncValue();
41833             
41834             this.deferFocus();
41835         }
41836     },
41837  // private
41838     mozKeyPress : function(e){
41839         if(e.ctrlKey){
41840             var c = e.getCharCode(), cmd;
41841           
41842             if(c > 0){
41843                 c = String.fromCharCode(c).toLowerCase();
41844                 switch(c){
41845                     case 'b':
41846                         cmd = 'bold';
41847                         break;
41848                     case 'i':
41849                         cmd = 'italic';
41850                         break;
41851                     
41852                     case 'u':
41853                         cmd = 'underline';
41854                         break;
41855                     
41856                     case 'v':
41857                         this.cleanUpPaste.defer(100, this);
41858                         return;
41859                         
41860                 }
41861                 if(cmd){
41862                     this.win.focus();
41863                     this.execCmd(cmd);
41864                     this.deferFocus();
41865                     e.preventDefault();
41866                 }
41867                 
41868             }
41869         }
41870     },
41871
41872     // private
41873     fixKeys : function(){ // load time branching for fastest keydown performance
41874         if(Roo.isIE){
41875             return function(e){
41876                 var k = e.getKey(), r;
41877                 if(k == e.TAB){
41878                     e.stopEvent();
41879                     r = this.doc.selection.createRange();
41880                     if(r){
41881                         r.collapse(true);
41882                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41883                         this.deferFocus();
41884                     }
41885                     return;
41886                 }
41887                 
41888                 if(k == e.ENTER){
41889                     r = this.doc.selection.createRange();
41890                     if(r){
41891                         var target = r.parentElement();
41892                         if(!target || target.tagName.toLowerCase() != 'li'){
41893                             e.stopEvent();
41894                             r.pasteHTML('<br />');
41895                             r.collapse(false);
41896                             r.select();
41897                         }
41898                     }
41899                 }
41900                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41901                     this.cleanUpPaste.defer(100, this);
41902                     return;
41903                 }
41904                 
41905                 
41906             };
41907         }else if(Roo.isOpera){
41908             return function(e){
41909                 var k = e.getKey();
41910                 if(k == e.TAB){
41911                     e.stopEvent();
41912                     this.win.focus();
41913                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41914                     this.deferFocus();
41915                 }
41916                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41917                     this.cleanUpPaste.defer(100, this);
41918                     return;
41919                 }
41920                 
41921             };
41922         }else if(Roo.isSafari){
41923             return function(e){
41924                 var k = e.getKey();
41925                 
41926                 if(k == e.TAB){
41927                     e.stopEvent();
41928                     this.execCmd('InsertText','\t');
41929                     this.deferFocus();
41930                     return;
41931                 }
41932                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41933                     this.cleanUpPaste.defer(100, this);
41934                     return;
41935                 }
41936                 
41937              };
41938         }
41939     }(),
41940     
41941     getAllAncestors: function()
41942     {
41943         var p = this.getSelectedNode();
41944         var a = [];
41945         if (!p) {
41946             a.push(p); // push blank onto stack..
41947             p = this.getParentElement();
41948         }
41949         
41950         
41951         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41952             a.push(p);
41953             p = p.parentNode;
41954         }
41955         a.push(this.doc.body);
41956         return a;
41957     },
41958     lastSel : false,
41959     lastSelNode : false,
41960     
41961     
41962     getSelection : function() 
41963     {
41964         this.assignDocWin();
41965         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41966     },
41967     
41968     getSelectedNode: function() 
41969     {
41970         // this may only work on Gecko!!!
41971         
41972         // should we cache this!!!!
41973         
41974         
41975         
41976          
41977         var range = this.createRange(this.getSelection()).cloneRange();
41978         
41979         if (Roo.isIE) {
41980             var parent = range.parentElement();
41981             while (true) {
41982                 var testRange = range.duplicate();
41983                 testRange.moveToElementText(parent);
41984                 if (testRange.inRange(range)) {
41985                     break;
41986                 }
41987                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41988                     break;
41989                 }
41990                 parent = parent.parentElement;
41991             }
41992             return parent;
41993         }
41994         
41995         // is ancestor a text element.
41996         var ac =  range.commonAncestorContainer;
41997         if (ac.nodeType == 3) {
41998             ac = ac.parentNode;
41999         }
42000         
42001         var ar = ac.childNodes;
42002          
42003         var nodes = [];
42004         var other_nodes = [];
42005         var has_other_nodes = false;
42006         for (var i=0;i<ar.length;i++) {
42007             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
42008                 continue;
42009             }
42010             // fullly contained node.
42011             
42012             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
42013                 nodes.push(ar[i]);
42014                 continue;
42015             }
42016             
42017             // probably selected..
42018             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
42019                 other_nodes.push(ar[i]);
42020                 continue;
42021             }
42022             // outer..
42023             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
42024                 continue;
42025             }
42026             
42027             
42028             has_other_nodes = true;
42029         }
42030         if (!nodes.length && other_nodes.length) {
42031             nodes= other_nodes;
42032         }
42033         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
42034             return false;
42035         }
42036         
42037         return nodes[0];
42038     },
42039     createRange: function(sel)
42040     {
42041         // this has strange effects when using with 
42042         // top toolbar - not sure if it's a great idea.
42043         //this.editor.contentWindow.focus();
42044         if (typeof sel != "undefined") {
42045             try {
42046                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
42047             } catch(e) {
42048                 return this.doc.createRange();
42049             }
42050         } else {
42051             return this.doc.createRange();
42052         }
42053     },
42054     getParentElement: function()
42055     {
42056         
42057         this.assignDocWin();
42058         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
42059         
42060         var range = this.createRange(sel);
42061          
42062         try {
42063             var p = range.commonAncestorContainer;
42064             while (p.nodeType == 3) { // text node
42065                 p = p.parentNode;
42066             }
42067             return p;
42068         } catch (e) {
42069             return null;
42070         }
42071     
42072     },
42073     /***
42074      *
42075      * Range intersection.. the hard stuff...
42076      *  '-1' = before
42077      *  '0' = hits..
42078      *  '1' = after.
42079      *         [ -- selected range --- ]
42080      *   [fail]                        [fail]
42081      *
42082      *    basically..
42083      *      if end is before start or  hits it. fail.
42084      *      if start is after end or hits it fail.
42085      *
42086      *   if either hits (but other is outside. - then it's not 
42087      *   
42088      *    
42089      **/
42090     
42091     
42092     // @see http://www.thismuchiknow.co.uk/?p=64.
42093     rangeIntersectsNode : function(range, node)
42094     {
42095         var nodeRange = node.ownerDocument.createRange();
42096         try {
42097             nodeRange.selectNode(node);
42098         } catch (e) {
42099             nodeRange.selectNodeContents(node);
42100         }
42101     
42102         var rangeStartRange = range.cloneRange();
42103         rangeStartRange.collapse(true);
42104     
42105         var rangeEndRange = range.cloneRange();
42106         rangeEndRange.collapse(false);
42107     
42108         var nodeStartRange = nodeRange.cloneRange();
42109         nodeStartRange.collapse(true);
42110     
42111         var nodeEndRange = nodeRange.cloneRange();
42112         nodeEndRange.collapse(false);
42113     
42114         return rangeStartRange.compareBoundaryPoints(
42115                  Range.START_TO_START, nodeEndRange) == -1 &&
42116                rangeEndRange.compareBoundaryPoints(
42117                  Range.START_TO_START, nodeStartRange) == 1;
42118         
42119          
42120     },
42121     rangeCompareNode : function(range, node)
42122     {
42123         var nodeRange = node.ownerDocument.createRange();
42124         try {
42125             nodeRange.selectNode(node);
42126         } catch (e) {
42127             nodeRange.selectNodeContents(node);
42128         }
42129         
42130         
42131         range.collapse(true);
42132     
42133         nodeRange.collapse(true);
42134      
42135         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42136         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42137          
42138         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42139         
42140         var nodeIsBefore   =  ss == 1;
42141         var nodeIsAfter    = ee == -1;
42142         
42143         if (nodeIsBefore && nodeIsAfter) {
42144             return 0; // outer
42145         }
42146         if (!nodeIsBefore && nodeIsAfter) {
42147             return 1; //right trailed.
42148         }
42149         
42150         if (nodeIsBefore && !nodeIsAfter) {
42151             return 2;  // left trailed.
42152         }
42153         // fully contined.
42154         return 3;
42155     },
42156
42157     // private? - in a new class?
42158     cleanUpPaste :  function()
42159     {
42160         // cleans up the whole document..
42161         Roo.log('cleanuppaste');
42162         
42163         this.cleanUpChildren(this.doc.body);
42164         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42165         if (clean != this.doc.body.innerHTML) {
42166             this.doc.body.innerHTML = clean;
42167         }
42168         
42169     },
42170     
42171     cleanWordChars : function(input) {// change the chars to hex code
42172         var he = Roo.HtmlEditorCore;
42173         
42174         var output = input;
42175         Roo.each(he.swapCodes, function(sw) { 
42176             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42177             
42178             output = output.replace(swapper, sw[1]);
42179         });
42180         
42181         return output;
42182     },
42183     
42184     
42185     cleanUpChildren : function (n)
42186     {
42187         if (!n.childNodes.length) {
42188             return;
42189         }
42190         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42191            this.cleanUpChild(n.childNodes[i]);
42192         }
42193     },
42194     
42195     
42196         
42197     
42198     cleanUpChild : function (node)
42199     {
42200         var ed = this;
42201         //console.log(node);
42202         if (node.nodeName == "#text") {
42203             // clean up silly Windows -- stuff?
42204             return; 
42205         }
42206         if (node.nodeName == "#comment") {
42207             node.parentNode.removeChild(node);
42208             // clean up silly Windows -- stuff?
42209             return; 
42210         }
42211         var lcname = node.tagName.toLowerCase();
42212         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42213         // whitelist of tags..
42214         
42215         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42216             // remove node.
42217             node.parentNode.removeChild(node);
42218             return;
42219             
42220         }
42221         
42222         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42223         
42224         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42225         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42226         
42227         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42228         //    remove_keep_children = true;
42229         //}
42230         
42231         if (remove_keep_children) {
42232             this.cleanUpChildren(node);
42233             // inserts everything just before this node...
42234             while (node.childNodes.length) {
42235                 var cn = node.childNodes[0];
42236                 node.removeChild(cn);
42237                 node.parentNode.insertBefore(cn, node);
42238             }
42239             node.parentNode.removeChild(node);
42240             return;
42241         }
42242         
42243         if (!node.attributes || !node.attributes.length) {
42244             this.cleanUpChildren(node);
42245             return;
42246         }
42247         
42248         function cleanAttr(n,v)
42249         {
42250             
42251             if (v.match(/^\./) || v.match(/^\//)) {
42252                 return;
42253             }
42254             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42255                 return;
42256             }
42257             if (v.match(/^#/)) {
42258                 return;
42259             }
42260 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42261             node.removeAttribute(n);
42262             
42263         }
42264         
42265         var cwhite = this.cwhite;
42266         var cblack = this.cblack;
42267             
42268         function cleanStyle(n,v)
42269         {
42270             if (v.match(/expression/)) { //XSS?? should we even bother..
42271                 node.removeAttribute(n);
42272                 return;
42273             }
42274             
42275             var parts = v.split(/;/);
42276             var clean = [];
42277             
42278             Roo.each(parts, function(p) {
42279                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42280                 if (!p.length) {
42281                     return true;
42282                 }
42283                 var l = p.split(':').shift().replace(/\s+/g,'');
42284                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42285                 
42286                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42287 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42288                     //node.removeAttribute(n);
42289                     return true;
42290                 }
42291                 //Roo.log()
42292                 // only allow 'c whitelisted system attributes'
42293                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42294 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42295                     //node.removeAttribute(n);
42296                     return true;
42297                 }
42298                 
42299                 
42300                  
42301                 
42302                 clean.push(p);
42303                 return true;
42304             });
42305             if (clean.length) { 
42306                 node.setAttribute(n, clean.join(';'));
42307             } else {
42308                 node.removeAttribute(n);
42309             }
42310             
42311         }
42312         
42313         
42314         for (var i = node.attributes.length-1; i > -1 ; i--) {
42315             var a = node.attributes[i];
42316             //console.log(a);
42317             
42318             if (a.name.toLowerCase().substr(0,2)=='on')  {
42319                 node.removeAttribute(a.name);
42320                 continue;
42321             }
42322             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42323                 node.removeAttribute(a.name);
42324                 continue;
42325             }
42326             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42327                 cleanAttr(a.name,a.value); // fixme..
42328                 continue;
42329             }
42330             if (a.name == 'style') {
42331                 cleanStyle(a.name,a.value);
42332                 continue;
42333             }
42334             /// clean up MS crap..
42335             // tecnically this should be a list of valid class'es..
42336             
42337             
42338             if (a.name == 'class') {
42339                 if (a.value.match(/^Mso/)) {
42340                     node.className = '';
42341                 }
42342                 
42343                 if (a.value.match(/body/)) {
42344                     node.className = '';
42345                 }
42346                 continue;
42347             }
42348             
42349             // style cleanup!?
42350             // class cleanup?
42351             
42352         }
42353         
42354         
42355         this.cleanUpChildren(node);
42356         
42357         
42358     },
42359     
42360     /**
42361      * Clean up MS wordisms...
42362      */
42363     cleanWord : function(node)
42364     {
42365         
42366         
42367         if (!node) {
42368             this.cleanWord(this.doc.body);
42369             return;
42370         }
42371         if (node.nodeName == "#text") {
42372             // clean up silly Windows -- stuff?
42373             return; 
42374         }
42375         if (node.nodeName == "#comment") {
42376             node.parentNode.removeChild(node);
42377             // clean up silly Windows -- stuff?
42378             return; 
42379         }
42380         
42381         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42382             node.parentNode.removeChild(node);
42383             return;
42384         }
42385         
42386         // remove - but keep children..
42387         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42388             while (node.childNodes.length) {
42389                 var cn = node.childNodes[0];
42390                 node.removeChild(cn);
42391                 node.parentNode.insertBefore(cn, node);
42392             }
42393             node.parentNode.removeChild(node);
42394             this.iterateChildren(node, this.cleanWord);
42395             return;
42396         }
42397         // clean styles
42398         if (node.className.length) {
42399             
42400             var cn = node.className.split(/\W+/);
42401             var cna = [];
42402             Roo.each(cn, function(cls) {
42403                 if (cls.match(/Mso[a-zA-Z]+/)) {
42404                     return;
42405                 }
42406                 cna.push(cls);
42407             });
42408             node.className = cna.length ? cna.join(' ') : '';
42409             if (!cna.length) {
42410                 node.removeAttribute("class");
42411             }
42412         }
42413         
42414         if (node.hasAttribute("lang")) {
42415             node.removeAttribute("lang");
42416         }
42417         
42418         if (node.hasAttribute("style")) {
42419             
42420             var styles = node.getAttribute("style").split(";");
42421             var nstyle = [];
42422             Roo.each(styles, function(s) {
42423                 if (!s.match(/:/)) {
42424                     return;
42425                 }
42426                 var kv = s.split(":");
42427                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42428                     return;
42429                 }
42430                 // what ever is left... we allow.
42431                 nstyle.push(s);
42432             });
42433             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42434             if (!nstyle.length) {
42435                 node.removeAttribute('style');
42436             }
42437         }
42438         this.iterateChildren(node, this.cleanWord);
42439         
42440         
42441         
42442     },
42443     /**
42444      * iterateChildren of a Node, calling fn each time, using this as the scole..
42445      * @param {DomNode} node node to iterate children of.
42446      * @param {Function} fn method of this class to call on each item.
42447      */
42448     iterateChildren : function(node, fn)
42449     {
42450         if (!node.childNodes.length) {
42451                 return;
42452         }
42453         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42454            fn.call(this, node.childNodes[i])
42455         }
42456     },
42457     
42458     
42459     /**
42460      * cleanTableWidths.
42461      *
42462      * Quite often pasting from word etc.. results in tables with column and widths.
42463      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42464      *
42465      */
42466     cleanTableWidths : function(node)
42467     {
42468          
42469          
42470         if (!node) {
42471             this.cleanTableWidths(this.doc.body);
42472             return;
42473         }
42474         
42475         // ignore list...
42476         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42477             return; 
42478         }
42479         Roo.log(node.tagName);
42480         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42481             this.iterateChildren(node, this.cleanTableWidths);
42482             return;
42483         }
42484         if (node.hasAttribute('width')) {
42485             node.removeAttribute('width');
42486         }
42487         
42488          
42489         if (node.hasAttribute("style")) {
42490             // pretty basic...
42491             
42492             var styles = node.getAttribute("style").split(";");
42493             var nstyle = [];
42494             Roo.each(styles, function(s) {
42495                 if (!s.match(/:/)) {
42496                     return;
42497                 }
42498                 var kv = s.split(":");
42499                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42500                     return;
42501                 }
42502                 // what ever is left... we allow.
42503                 nstyle.push(s);
42504             });
42505             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42506             if (!nstyle.length) {
42507                 node.removeAttribute('style');
42508             }
42509         }
42510         
42511         this.iterateChildren(node, this.cleanTableWidths);
42512         
42513         
42514     },
42515     
42516     
42517     
42518     
42519     domToHTML : function(currentElement, depth, nopadtext) {
42520         
42521         depth = depth || 0;
42522         nopadtext = nopadtext || false;
42523     
42524         if (!currentElement) {
42525             return this.domToHTML(this.doc.body);
42526         }
42527         
42528         //Roo.log(currentElement);
42529         var j;
42530         var allText = false;
42531         var nodeName = currentElement.nodeName;
42532         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42533         
42534         if  (nodeName == '#text') {
42535             
42536             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42537         }
42538         
42539         
42540         var ret = '';
42541         if (nodeName != 'BODY') {
42542              
42543             var i = 0;
42544             // Prints the node tagName, such as <A>, <IMG>, etc
42545             if (tagName) {
42546                 var attr = [];
42547                 for(i = 0; i < currentElement.attributes.length;i++) {
42548                     // quoting?
42549                     var aname = currentElement.attributes.item(i).name;
42550                     if (!currentElement.attributes.item(i).value.length) {
42551                         continue;
42552                     }
42553                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42554                 }
42555                 
42556                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42557             } 
42558             else {
42559                 
42560                 // eack
42561             }
42562         } else {
42563             tagName = false;
42564         }
42565         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42566             return ret;
42567         }
42568         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42569             nopadtext = true;
42570         }
42571         
42572         
42573         // Traverse the tree
42574         i = 0;
42575         var currentElementChild = currentElement.childNodes.item(i);
42576         var allText = true;
42577         var innerHTML  = '';
42578         lastnode = '';
42579         while (currentElementChild) {
42580             // Formatting code (indent the tree so it looks nice on the screen)
42581             var nopad = nopadtext;
42582             if (lastnode == 'SPAN') {
42583                 nopad  = true;
42584             }
42585             // text
42586             if  (currentElementChild.nodeName == '#text') {
42587                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42588                 toadd = nopadtext ? toadd : toadd.trim();
42589                 if (!nopad && toadd.length > 80) {
42590                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42591                 }
42592                 innerHTML  += toadd;
42593                 
42594                 i++;
42595                 currentElementChild = currentElement.childNodes.item(i);
42596                 lastNode = '';
42597                 continue;
42598             }
42599             allText = false;
42600             
42601             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42602                 
42603             // Recursively traverse the tree structure of the child node
42604             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42605             lastnode = currentElementChild.nodeName;
42606             i++;
42607             currentElementChild=currentElement.childNodes.item(i);
42608         }
42609         
42610         ret += innerHTML;
42611         
42612         if (!allText) {
42613                 // The remaining code is mostly for formatting the tree
42614             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42615         }
42616         
42617         
42618         if (tagName) {
42619             ret+= "</"+tagName+">";
42620         }
42621         return ret;
42622         
42623     },
42624         
42625     applyBlacklists : function()
42626     {
42627         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42628         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42629         
42630         this.white = [];
42631         this.black = [];
42632         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42633             if (b.indexOf(tag) > -1) {
42634                 return;
42635             }
42636             this.white.push(tag);
42637             
42638         }, this);
42639         
42640         Roo.each(w, function(tag) {
42641             if (b.indexOf(tag) > -1) {
42642                 return;
42643             }
42644             if (this.white.indexOf(tag) > -1) {
42645                 return;
42646             }
42647             this.white.push(tag);
42648             
42649         }, this);
42650         
42651         
42652         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42653             if (w.indexOf(tag) > -1) {
42654                 return;
42655             }
42656             this.black.push(tag);
42657             
42658         }, this);
42659         
42660         Roo.each(b, function(tag) {
42661             if (w.indexOf(tag) > -1) {
42662                 return;
42663             }
42664             if (this.black.indexOf(tag) > -1) {
42665                 return;
42666             }
42667             this.black.push(tag);
42668             
42669         }, this);
42670         
42671         
42672         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42673         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42674         
42675         this.cwhite = [];
42676         this.cblack = [];
42677         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42678             if (b.indexOf(tag) > -1) {
42679                 return;
42680             }
42681             this.cwhite.push(tag);
42682             
42683         }, this);
42684         
42685         Roo.each(w, function(tag) {
42686             if (b.indexOf(tag) > -1) {
42687                 return;
42688             }
42689             if (this.cwhite.indexOf(tag) > -1) {
42690                 return;
42691             }
42692             this.cwhite.push(tag);
42693             
42694         }, this);
42695         
42696         
42697         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42698             if (w.indexOf(tag) > -1) {
42699                 return;
42700             }
42701             this.cblack.push(tag);
42702             
42703         }, this);
42704         
42705         Roo.each(b, function(tag) {
42706             if (w.indexOf(tag) > -1) {
42707                 return;
42708             }
42709             if (this.cblack.indexOf(tag) > -1) {
42710                 return;
42711             }
42712             this.cblack.push(tag);
42713             
42714         }, this);
42715     },
42716     
42717     setStylesheets : function(stylesheets)
42718     {
42719         if(typeof(stylesheets) == 'string'){
42720             Roo.get(this.iframe.contentDocument.head).createChild({
42721                 tag : 'link',
42722                 rel : 'stylesheet',
42723                 type : 'text/css',
42724                 href : stylesheets
42725             });
42726             
42727             return;
42728         }
42729         var _this = this;
42730      
42731         Roo.each(stylesheets, function(s) {
42732             if(!s.length){
42733                 return;
42734             }
42735             
42736             Roo.get(_this.iframe.contentDocument.head).createChild({
42737                 tag : 'link',
42738                 rel : 'stylesheet',
42739                 type : 'text/css',
42740                 href : s
42741             });
42742         });
42743
42744         
42745     },
42746     
42747     removeStylesheets : function()
42748     {
42749         var _this = this;
42750         
42751         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42752             s.remove();
42753         });
42754     }
42755     
42756     // hide stuff that is not compatible
42757     /**
42758      * @event blur
42759      * @hide
42760      */
42761     /**
42762      * @event change
42763      * @hide
42764      */
42765     /**
42766      * @event focus
42767      * @hide
42768      */
42769     /**
42770      * @event specialkey
42771      * @hide
42772      */
42773     /**
42774      * @cfg {String} fieldClass @hide
42775      */
42776     /**
42777      * @cfg {String} focusClass @hide
42778      */
42779     /**
42780      * @cfg {String} autoCreate @hide
42781      */
42782     /**
42783      * @cfg {String} inputType @hide
42784      */
42785     /**
42786      * @cfg {String} invalidClass @hide
42787      */
42788     /**
42789      * @cfg {String} invalidText @hide
42790      */
42791     /**
42792      * @cfg {String} msgFx @hide
42793      */
42794     /**
42795      * @cfg {String} validateOnBlur @hide
42796      */
42797 });
42798
42799 Roo.HtmlEditorCore.white = [
42800         'area', 'br', 'img', 'input', 'hr', 'wbr',
42801         
42802        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42803        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42804        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42805        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42806        'table',   'ul',         'xmp', 
42807        
42808        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42809       'thead',   'tr', 
42810      
42811       'dir', 'menu', 'ol', 'ul', 'dl',
42812        
42813       'embed',  'object'
42814 ];
42815
42816
42817 Roo.HtmlEditorCore.black = [
42818     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42819         'applet', // 
42820         'base',   'basefont', 'bgsound', 'blink',  'body', 
42821         'frame',  'frameset', 'head',    'html',   'ilayer', 
42822         'iframe', 'layer',  'link',     'meta',    'object',   
42823         'script', 'style' ,'title',  'xml' // clean later..
42824 ];
42825 Roo.HtmlEditorCore.clean = [
42826     'script', 'style', 'title', 'xml'
42827 ];
42828 Roo.HtmlEditorCore.remove = [
42829     'font'
42830 ];
42831 // attributes..
42832
42833 Roo.HtmlEditorCore.ablack = [
42834     'on'
42835 ];
42836     
42837 Roo.HtmlEditorCore.aclean = [ 
42838     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42839 ];
42840
42841 // protocols..
42842 Roo.HtmlEditorCore.pwhite= [
42843         'http',  'https',  'mailto'
42844 ];
42845
42846 // white listed style attributes.
42847 Roo.HtmlEditorCore.cwhite= [
42848       //  'text-align', /// default is to allow most things..
42849       
42850          
42851 //        'font-size'//??
42852 ];
42853
42854 // black listed style attributes.
42855 Roo.HtmlEditorCore.cblack= [
42856       //  'font-size' -- this can be set by the project 
42857 ];
42858
42859
42860 Roo.HtmlEditorCore.swapCodes   =[ 
42861     [    8211, "--" ], 
42862     [    8212, "--" ], 
42863     [    8216,  "'" ],  
42864     [    8217, "'" ],  
42865     [    8220, '"' ],  
42866     [    8221, '"' ],  
42867     [    8226, "*" ],  
42868     [    8230, "..." ]
42869 ]; 
42870
42871     //<script type="text/javascript">
42872
42873 /*
42874  * Ext JS Library 1.1.1
42875  * Copyright(c) 2006-2007, Ext JS, LLC.
42876  * Licence LGPL
42877  * 
42878  */
42879  
42880  
42881 Roo.form.HtmlEditor = function(config){
42882     
42883     
42884     
42885     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42886     
42887     if (!this.toolbars) {
42888         this.toolbars = [];
42889     }
42890     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42891     
42892     
42893 };
42894
42895 /**
42896  * @class Roo.form.HtmlEditor
42897  * @extends Roo.form.Field
42898  * Provides a lightweight HTML Editor component.
42899  *
42900  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42901  * 
42902  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42903  * supported by this editor.</b><br/><br/>
42904  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42905  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42906  */
42907 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42908     /**
42909      * @cfg {Boolean} clearUp
42910      */
42911     clearUp : true,
42912       /**
42913      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42914      */
42915     toolbars : false,
42916    
42917      /**
42918      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42919      *                        Roo.resizable.
42920      */
42921     resizable : false,
42922      /**
42923      * @cfg {Number} height (in pixels)
42924      */   
42925     height: 300,
42926    /**
42927      * @cfg {Number} width (in pixels)
42928      */   
42929     width: 500,
42930     
42931     /**
42932      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42933      * 
42934      */
42935     stylesheets: false,
42936     
42937     
42938      /**
42939      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42940      * 
42941      */
42942     cblack: false,
42943     /**
42944      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42945      * 
42946      */
42947     cwhite: false,
42948     
42949      /**
42950      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42951      * 
42952      */
42953     black: false,
42954     /**
42955      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42956      * 
42957      */
42958     white: false,
42959     
42960     // id of frame..
42961     frameId: false,
42962     
42963     // private properties
42964     validationEvent : false,
42965     deferHeight: true,
42966     initialized : false,
42967     activated : false,
42968     
42969     onFocus : Roo.emptyFn,
42970     iframePad:3,
42971     hideMode:'offsets',
42972     
42973     actionMode : 'container', // defaults to hiding it...
42974     
42975     defaultAutoCreate : { // modified by initCompnoent..
42976         tag: "textarea",
42977         style:"width:500px;height:300px;",
42978         autocomplete: "new-password"
42979     },
42980
42981     // private
42982     initComponent : function(){
42983         this.addEvents({
42984             /**
42985              * @event initialize
42986              * Fires when the editor is fully initialized (including the iframe)
42987              * @param {HtmlEditor} this
42988              */
42989             initialize: true,
42990             /**
42991              * @event activate
42992              * Fires when the editor is first receives the focus. Any insertion must wait
42993              * until after this event.
42994              * @param {HtmlEditor} this
42995              */
42996             activate: true,
42997              /**
42998              * @event beforesync
42999              * Fires before the textarea is updated with content from the editor iframe. Return false
43000              * to cancel the sync.
43001              * @param {HtmlEditor} this
43002              * @param {String} html
43003              */
43004             beforesync: true,
43005              /**
43006              * @event beforepush
43007              * Fires before the iframe editor is updated with content from the textarea. Return false
43008              * to cancel the push.
43009              * @param {HtmlEditor} this
43010              * @param {String} html
43011              */
43012             beforepush: true,
43013              /**
43014              * @event sync
43015              * Fires when the textarea is updated with content from the editor iframe.
43016              * @param {HtmlEditor} this
43017              * @param {String} html
43018              */
43019             sync: true,
43020              /**
43021              * @event push
43022              * Fires when the iframe editor is updated with content from the textarea.
43023              * @param {HtmlEditor} this
43024              * @param {String} html
43025              */
43026             push: true,
43027              /**
43028              * @event editmodechange
43029              * Fires when the editor switches edit modes
43030              * @param {HtmlEditor} this
43031              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
43032              */
43033             editmodechange: true,
43034             /**
43035              * @event editorevent
43036              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43037              * @param {HtmlEditor} this
43038              */
43039             editorevent: true,
43040             /**
43041              * @event firstfocus
43042              * Fires when on first focus - needed by toolbars..
43043              * @param {HtmlEditor} this
43044              */
43045             firstfocus: true,
43046             /**
43047              * @event autosave
43048              * Auto save the htmlEditor value as a file into Events
43049              * @param {HtmlEditor} this
43050              */
43051             autosave: true,
43052             /**
43053              * @event savedpreview
43054              * preview the saved version of htmlEditor
43055              * @param {HtmlEditor} this
43056              */
43057             savedpreview: true,
43058             
43059             /**
43060             * @event stylesheetsclick
43061             * Fires when press the Sytlesheets button
43062             * @param {Roo.HtmlEditorCore} this
43063             */
43064             stylesheetsclick: true
43065         });
43066         this.defaultAutoCreate =  {
43067             tag: "textarea",
43068             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
43069             autocomplete: "new-password"
43070         };
43071     },
43072
43073     /**
43074      * Protected method that will not generally be called directly. It
43075      * is called when the editor creates its toolbar. Override this method if you need to
43076      * add custom toolbar buttons.
43077      * @param {HtmlEditor} editor
43078      */
43079     createToolbar : function(editor){
43080         Roo.log("create toolbars");
43081         if (!editor.toolbars || !editor.toolbars.length) {
43082             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43083         }
43084         
43085         for (var i =0 ; i < editor.toolbars.length;i++) {
43086             editor.toolbars[i] = Roo.factory(
43087                     typeof(editor.toolbars[i]) == 'string' ?
43088                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43089                 Roo.form.HtmlEditor);
43090             editor.toolbars[i].init(editor);
43091         }
43092          
43093         
43094     },
43095
43096      
43097     // private
43098     onRender : function(ct, position)
43099     {
43100         var _t = this;
43101         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43102         
43103         this.wrap = this.el.wrap({
43104             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43105         });
43106         
43107         this.editorcore.onRender(ct, position);
43108          
43109         if (this.resizable) {
43110             this.resizeEl = new Roo.Resizable(this.wrap, {
43111                 pinned : true,
43112                 wrap: true,
43113                 dynamic : true,
43114                 minHeight : this.height,
43115                 height: this.height,
43116                 handles : this.resizable,
43117                 width: this.width,
43118                 listeners : {
43119                     resize : function(r, w, h) {
43120                         _t.onResize(w,h); // -something
43121                     }
43122                 }
43123             });
43124             
43125         }
43126         this.createToolbar(this);
43127        
43128         
43129         if(!this.width){
43130             this.setSize(this.wrap.getSize());
43131         }
43132         if (this.resizeEl) {
43133             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43134             // should trigger onReize..
43135         }
43136         
43137         this.keyNav = new Roo.KeyNav(this.el, {
43138             
43139             "tab" : function(e){
43140                 e.preventDefault();
43141                 
43142                 var value = this.getValue();
43143                 
43144                 var start = this.el.dom.selectionStart;
43145                 var end = this.el.dom.selectionEnd;
43146                 
43147                 if(!e.shiftKey){
43148                     
43149                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43150                     this.el.dom.setSelectionRange(end + 1, end + 1);
43151                     return;
43152                 }
43153                 
43154                 var f = value.substring(0, start).split("\t");
43155                 
43156                 if(f.pop().length != 0){
43157                     return;
43158                 }
43159                 
43160                 this.setValue(f.join("\t") + value.substring(end));
43161                 this.el.dom.setSelectionRange(start - 1, start - 1);
43162                 
43163             },
43164             
43165             "home" : function(e){
43166                 e.preventDefault();
43167                 
43168                 var curr = this.el.dom.selectionStart;
43169                 var lines = this.getValue().split("\n");
43170                 
43171                 if(!lines.length){
43172                     return;
43173                 }
43174                 
43175                 if(e.ctrlKey){
43176                     this.el.dom.setSelectionRange(0, 0);
43177                     return;
43178                 }
43179                 
43180                 var pos = 0;
43181                 
43182                 for (var i = 0; i < lines.length;i++) {
43183                     pos += lines[i].length;
43184                     
43185                     if(i != 0){
43186                         pos += 1;
43187                     }
43188                     
43189                     if(pos < curr){
43190                         continue;
43191                     }
43192                     
43193                     pos -= lines[i].length;
43194                     
43195                     break;
43196                 }
43197                 
43198                 if(!e.shiftKey){
43199                     this.el.dom.setSelectionRange(pos, pos);
43200                     return;
43201                 }
43202                 
43203                 this.el.dom.selectionStart = pos;
43204                 this.el.dom.selectionEnd = curr;
43205             },
43206             
43207             "end" : function(e){
43208                 e.preventDefault();
43209                 
43210                 var curr = this.el.dom.selectionStart;
43211                 var lines = this.getValue().split("\n");
43212                 
43213                 if(!lines.length){
43214                     return;
43215                 }
43216                 
43217                 if(e.ctrlKey){
43218                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43219                     return;
43220                 }
43221                 
43222                 var pos = 0;
43223                 
43224                 for (var i = 0; i < lines.length;i++) {
43225                     
43226                     pos += lines[i].length;
43227                     
43228                     if(i != 0){
43229                         pos += 1;
43230                     }
43231                     
43232                     if(pos < curr){
43233                         continue;
43234                     }
43235                     
43236                     break;
43237                 }
43238                 
43239                 if(!e.shiftKey){
43240                     this.el.dom.setSelectionRange(pos, pos);
43241                     return;
43242                 }
43243                 
43244                 this.el.dom.selectionStart = curr;
43245                 this.el.dom.selectionEnd = pos;
43246             },
43247
43248             scope : this,
43249
43250             doRelay : function(foo, bar, hname){
43251                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43252             },
43253
43254             forceKeyDown: true
43255         });
43256         
43257 //        if(this.autosave && this.w){
43258 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43259 //        }
43260     },
43261
43262     // private
43263     onResize : function(w, h)
43264     {
43265         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43266         var ew = false;
43267         var eh = false;
43268         
43269         if(this.el ){
43270             if(typeof w == 'number'){
43271                 var aw = w - this.wrap.getFrameWidth('lr');
43272                 this.el.setWidth(this.adjustWidth('textarea', aw));
43273                 ew = aw;
43274             }
43275             if(typeof h == 'number'){
43276                 var tbh = 0;
43277                 for (var i =0; i < this.toolbars.length;i++) {
43278                     // fixme - ask toolbars for heights?
43279                     tbh += this.toolbars[i].tb.el.getHeight();
43280                     if (this.toolbars[i].footer) {
43281                         tbh += this.toolbars[i].footer.el.getHeight();
43282                     }
43283                 }
43284                 
43285                 
43286                 
43287                 
43288                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43289                 ah -= 5; // knock a few pixes off for look..
43290 //                Roo.log(ah);
43291                 this.el.setHeight(this.adjustWidth('textarea', ah));
43292                 var eh = ah;
43293             }
43294         }
43295         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43296         this.editorcore.onResize(ew,eh);
43297         
43298     },
43299
43300     /**
43301      * Toggles the editor between standard and source edit mode.
43302      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43303      */
43304     toggleSourceEdit : function(sourceEditMode)
43305     {
43306         this.editorcore.toggleSourceEdit(sourceEditMode);
43307         
43308         if(this.editorcore.sourceEditMode){
43309             Roo.log('editor - showing textarea');
43310             
43311 //            Roo.log('in');
43312 //            Roo.log(this.syncValue());
43313             this.editorcore.syncValue();
43314             this.el.removeClass('x-hidden');
43315             this.el.dom.removeAttribute('tabIndex');
43316             this.el.focus();
43317             
43318             for (var i = 0; i < this.toolbars.length; i++) {
43319                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43320                     this.toolbars[i].tb.hide();
43321                     this.toolbars[i].footer.hide();
43322                 }
43323             }
43324             
43325         }else{
43326             Roo.log('editor - hiding textarea');
43327 //            Roo.log('out')
43328 //            Roo.log(this.pushValue()); 
43329             this.editorcore.pushValue();
43330             
43331             this.el.addClass('x-hidden');
43332             this.el.dom.setAttribute('tabIndex', -1);
43333             
43334             for (var i = 0; i < this.toolbars.length; i++) {
43335                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43336                     this.toolbars[i].tb.show();
43337                     this.toolbars[i].footer.show();
43338                 }
43339             }
43340             
43341             //this.deferFocus();
43342         }
43343         
43344         this.setSize(this.wrap.getSize());
43345         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43346         
43347         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43348     },
43349  
43350     // private (for BoxComponent)
43351     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43352
43353     // private (for BoxComponent)
43354     getResizeEl : function(){
43355         return this.wrap;
43356     },
43357
43358     // private (for BoxComponent)
43359     getPositionEl : function(){
43360         return this.wrap;
43361     },
43362
43363     // private
43364     initEvents : function(){
43365         this.originalValue = this.getValue();
43366     },
43367
43368     /**
43369      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43370      * @method
43371      */
43372     markInvalid : Roo.emptyFn,
43373     /**
43374      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43375      * @method
43376      */
43377     clearInvalid : Roo.emptyFn,
43378
43379     setValue : function(v){
43380         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43381         this.editorcore.pushValue();
43382     },
43383
43384      
43385     // private
43386     deferFocus : function(){
43387         this.focus.defer(10, this);
43388     },
43389
43390     // doc'ed in Field
43391     focus : function(){
43392         this.editorcore.focus();
43393         
43394     },
43395       
43396
43397     // private
43398     onDestroy : function(){
43399         
43400         
43401         
43402         if(this.rendered){
43403             
43404             for (var i =0; i < this.toolbars.length;i++) {
43405                 // fixme - ask toolbars for heights?
43406                 this.toolbars[i].onDestroy();
43407             }
43408             
43409             this.wrap.dom.innerHTML = '';
43410             this.wrap.remove();
43411         }
43412     },
43413
43414     // private
43415     onFirstFocus : function(){
43416         //Roo.log("onFirstFocus");
43417         this.editorcore.onFirstFocus();
43418          for (var i =0; i < this.toolbars.length;i++) {
43419             this.toolbars[i].onFirstFocus();
43420         }
43421         
43422     },
43423     
43424     // private
43425     syncValue : function()
43426     {
43427         this.editorcore.syncValue();
43428     },
43429     
43430     pushValue : function()
43431     {
43432         this.editorcore.pushValue();
43433     },
43434     
43435     setStylesheets : function(stylesheets)
43436     {
43437         this.editorcore.setStylesheets(stylesheets);
43438     },
43439     
43440     removeStylesheets : function()
43441     {
43442         this.editorcore.removeStylesheets();
43443     }
43444      
43445     
43446     // hide stuff that is not compatible
43447     /**
43448      * @event blur
43449      * @hide
43450      */
43451     /**
43452      * @event change
43453      * @hide
43454      */
43455     /**
43456      * @event focus
43457      * @hide
43458      */
43459     /**
43460      * @event specialkey
43461      * @hide
43462      */
43463     /**
43464      * @cfg {String} fieldClass @hide
43465      */
43466     /**
43467      * @cfg {String} focusClass @hide
43468      */
43469     /**
43470      * @cfg {String} autoCreate @hide
43471      */
43472     /**
43473      * @cfg {String} inputType @hide
43474      */
43475     /**
43476      * @cfg {String} invalidClass @hide
43477      */
43478     /**
43479      * @cfg {String} invalidText @hide
43480      */
43481     /**
43482      * @cfg {String} msgFx @hide
43483      */
43484     /**
43485      * @cfg {String} validateOnBlur @hide
43486      */
43487 });
43488  
43489     // <script type="text/javascript">
43490 /*
43491  * Based on
43492  * Ext JS Library 1.1.1
43493  * Copyright(c) 2006-2007, Ext JS, LLC.
43494  *  
43495  
43496  */
43497
43498 /**
43499  * @class Roo.form.HtmlEditorToolbar1
43500  * Basic Toolbar
43501  * 
43502  * Usage:
43503  *
43504  new Roo.form.HtmlEditor({
43505     ....
43506     toolbars : [
43507         new Roo.form.HtmlEditorToolbar1({
43508             disable : { fonts: 1 , format: 1, ..., ... , ...],
43509             btns : [ .... ]
43510         })
43511     }
43512      
43513  * 
43514  * @cfg {Object} disable List of elements to disable..
43515  * @cfg {Array} btns List of additional buttons.
43516  * 
43517  * 
43518  * NEEDS Extra CSS? 
43519  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43520  */
43521  
43522 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43523 {
43524     
43525     Roo.apply(this, config);
43526     
43527     // default disabled, based on 'good practice'..
43528     this.disable = this.disable || {};
43529     Roo.applyIf(this.disable, {
43530         fontSize : true,
43531         colors : true,
43532         specialElements : true
43533     });
43534     
43535     
43536     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43537     // dont call parent... till later.
43538 }
43539
43540 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43541     
43542     tb: false,
43543     
43544     rendered: false,
43545     
43546     editor : false,
43547     editorcore : false,
43548     /**
43549      * @cfg {Object} disable  List of toolbar elements to disable
43550          
43551      */
43552     disable : false,
43553     
43554     
43555      /**
43556      * @cfg {String} createLinkText The default text for the create link prompt
43557      */
43558     createLinkText : 'Please enter the URL for the link:',
43559     /**
43560      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43561      */
43562     defaultLinkValue : 'http:/'+'/',
43563    
43564     
43565       /**
43566      * @cfg {Array} fontFamilies An array of available font families
43567      */
43568     fontFamilies : [
43569         'Arial',
43570         'Courier New',
43571         'Tahoma',
43572         'Times New Roman',
43573         'Verdana'
43574     ],
43575     
43576     specialChars : [
43577            "&#169;",
43578           "&#174;",     
43579           "&#8482;",    
43580           "&#163;" ,    
43581          // "&#8212;",    
43582           "&#8230;",    
43583           "&#247;" ,    
43584         //  "&#225;" ,     ?? a acute?
43585            "&#8364;"    , //Euro
43586        //   "&#8220;"    ,
43587         //  "&#8221;"    ,
43588         //  "&#8226;"    ,
43589           "&#176;"  //   , // degrees
43590
43591          // "&#233;"     , // e ecute
43592          // "&#250;"     , // u ecute?
43593     ],
43594     
43595     specialElements : [
43596         {
43597             text: "Insert Table",
43598             xtype: 'MenuItem',
43599             xns : Roo.Menu,
43600             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43601                 
43602         },
43603         {    
43604             text: "Insert Image",
43605             xtype: 'MenuItem',
43606             xns : Roo.Menu,
43607             ihtml : '<img src="about:blank"/>'
43608             
43609         }
43610         
43611          
43612     ],
43613     
43614     
43615     inputElements : [ 
43616             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43617             "input:submit", "input:button", "select", "textarea", "label" ],
43618     formats : [
43619         ["p"] ,  
43620         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43621         ["pre"],[ "code"], 
43622         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43623         ['div'],['span']
43624     ],
43625     
43626     cleanStyles : [
43627         "font-size"
43628     ],
43629      /**
43630      * @cfg {String} defaultFont default font to use.
43631      */
43632     defaultFont: 'tahoma',
43633    
43634     fontSelect : false,
43635     
43636     
43637     formatCombo : false,
43638     
43639     init : function(editor)
43640     {
43641         this.editor = editor;
43642         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43643         var editorcore = this.editorcore;
43644         
43645         var _t = this;
43646         
43647         var fid = editorcore.frameId;
43648         var etb = this;
43649         function btn(id, toggle, handler){
43650             var xid = fid + '-'+ id ;
43651             return {
43652                 id : xid,
43653                 cmd : id,
43654                 cls : 'x-btn-icon x-edit-'+id,
43655                 enableToggle:toggle !== false,
43656                 scope: _t, // was editor...
43657                 handler:handler||_t.relayBtnCmd,
43658                 clickEvent:'mousedown',
43659                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43660                 tabIndex:-1
43661             };
43662         }
43663         
43664         
43665         
43666         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43667         this.tb = tb;
43668          // stop form submits
43669         tb.el.on('click', function(e){
43670             e.preventDefault(); // what does this do?
43671         });
43672
43673         if(!this.disable.font) { // && !Roo.isSafari){
43674             /* why no safari for fonts 
43675             editor.fontSelect = tb.el.createChild({
43676                 tag:'select',
43677                 tabIndex: -1,
43678                 cls:'x-font-select',
43679                 html: this.createFontOptions()
43680             });
43681             
43682             editor.fontSelect.on('change', function(){
43683                 var font = editor.fontSelect.dom.value;
43684                 editor.relayCmd('fontname', font);
43685                 editor.deferFocus();
43686             }, editor);
43687             
43688             tb.add(
43689                 editor.fontSelect.dom,
43690                 '-'
43691             );
43692             */
43693             
43694         };
43695         if(!this.disable.formats){
43696             this.formatCombo = new Roo.form.ComboBox({
43697                 store: new Roo.data.SimpleStore({
43698                     id : 'tag',
43699                     fields: ['tag'],
43700                     data : this.formats // from states.js
43701                 }),
43702                 blockFocus : true,
43703                 name : '',
43704                 //autoCreate : {tag: "div",  size: "20"},
43705                 displayField:'tag',
43706                 typeAhead: false,
43707                 mode: 'local',
43708                 editable : false,
43709                 triggerAction: 'all',
43710                 emptyText:'Add tag',
43711                 selectOnFocus:true,
43712                 width:135,
43713                 listeners : {
43714                     'select': function(c, r, i) {
43715                         editorcore.insertTag(r.get('tag'));
43716                         editor.focus();
43717                     }
43718                 }
43719
43720             });
43721             tb.addField(this.formatCombo);
43722             
43723         }
43724         
43725         if(!this.disable.format){
43726             tb.add(
43727                 btn('bold'),
43728                 btn('italic'),
43729                 btn('underline'),
43730                 btn('strikethrough')
43731             );
43732         };
43733         if(!this.disable.fontSize){
43734             tb.add(
43735                 '-',
43736                 
43737                 
43738                 btn('increasefontsize', false, editorcore.adjustFont),
43739                 btn('decreasefontsize', false, editorcore.adjustFont)
43740             );
43741         };
43742         
43743         
43744         if(!this.disable.colors){
43745             tb.add(
43746                 '-', {
43747                     id:editorcore.frameId +'-forecolor',
43748                     cls:'x-btn-icon x-edit-forecolor',
43749                     clickEvent:'mousedown',
43750                     tooltip: this.buttonTips['forecolor'] || undefined,
43751                     tabIndex:-1,
43752                     menu : new Roo.menu.ColorMenu({
43753                         allowReselect: true,
43754                         focus: Roo.emptyFn,
43755                         value:'000000',
43756                         plain:true,
43757                         selectHandler: function(cp, color){
43758                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43759                             editor.deferFocus();
43760                         },
43761                         scope: editorcore,
43762                         clickEvent:'mousedown'
43763                     })
43764                 }, {
43765                     id:editorcore.frameId +'backcolor',
43766                     cls:'x-btn-icon x-edit-backcolor',
43767                     clickEvent:'mousedown',
43768                     tooltip: this.buttonTips['backcolor'] || undefined,
43769                     tabIndex:-1,
43770                     menu : new Roo.menu.ColorMenu({
43771                         focus: Roo.emptyFn,
43772                         value:'FFFFFF',
43773                         plain:true,
43774                         allowReselect: true,
43775                         selectHandler: function(cp, color){
43776                             if(Roo.isGecko){
43777                                 editorcore.execCmd('useCSS', false);
43778                                 editorcore.execCmd('hilitecolor', color);
43779                                 editorcore.execCmd('useCSS', true);
43780                                 editor.deferFocus();
43781                             }else{
43782                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43783                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43784                                 editor.deferFocus();
43785                             }
43786                         },
43787                         scope:editorcore,
43788                         clickEvent:'mousedown'
43789                     })
43790                 }
43791             );
43792         };
43793         // now add all the items...
43794         
43795
43796         if(!this.disable.alignments){
43797             tb.add(
43798                 '-',
43799                 btn('justifyleft'),
43800                 btn('justifycenter'),
43801                 btn('justifyright')
43802             );
43803         };
43804
43805         //if(!Roo.isSafari){
43806             if(!this.disable.links){
43807                 tb.add(
43808                     '-',
43809                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43810                 );
43811             };
43812
43813             if(!this.disable.lists){
43814                 tb.add(
43815                     '-',
43816                     btn('insertorderedlist'),
43817                     btn('insertunorderedlist')
43818                 );
43819             }
43820             if(!this.disable.sourceEdit){
43821                 tb.add(
43822                     '-',
43823                     btn('sourceedit', true, function(btn){
43824                         this.toggleSourceEdit(btn.pressed);
43825                     })
43826                 );
43827             }
43828         //}
43829         
43830         var smenu = { };
43831         // special menu.. - needs to be tidied up..
43832         if (!this.disable.special) {
43833             smenu = {
43834                 text: "&#169;",
43835                 cls: 'x-edit-none',
43836                 
43837                 menu : {
43838                     items : []
43839                 }
43840             };
43841             for (var i =0; i < this.specialChars.length; i++) {
43842                 smenu.menu.items.push({
43843                     
43844                     html: this.specialChars[i],
43845                     handler: function(a,b) {
43846                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43847                         //editor.insertAtCursor(a.html);
43848                         
43849                     },
43850                     tabIndex:-1
43851                 });
43852             }
43853             
43854             
43855             tb.add(smenu);
43856             
43857             
43858         }
43859         
43860         var cmenu = { };
43861         if (!this.disable.cleanStyles) {
43862             cmenu = {
43863                 cls: 'x-btn-icon x-btn-clear',
43864                 
43865                 menu : {
43866                     items : []
43867                 }
43868             };
43869             for (var i =0; i < this.cleanStyles.length; i++) {
43870                 cmenu.menu.items.push({
43871                     actiontype : this.cleanStyles[i],
43872                     html: 'Remove ' + this.cleanStyles[i],
43873                     handler: function(a,b) {
43874 //                        Roo.log(a);
43875 //                        Roo.log(b);
43876                         var c = Roo.get(editorcore.doc.body);
43877                         c.select('[style]').each(function(s) {
43878                             s.dom.style.removeProperty(a.actiontype);
43879                         });
43880                         editorcore.syncValue();
43881                     },
43882                     tabIndex:-1
43883                 });
43884             }
43885              cmenu.menu.items.push({
43886                 actiontype : 'tablewidths',
43887                 html: 'Remove Table Widths',
43888                 handler: function(a,b) {
43889                     editorcore.cleanTableWidths();
43890                     editorcore.syncValue();
43891                 },
43892                 tabIndex:-1
43893             });
43894             cmenu.menu.items.push({
43895                 actiontype : 'word',
43896                 html: 'Remove MS Word Formating',
43897                 handler: function(a,b) {
43898                     editorcore.cleanWord();
43899                     editorcore.syncValue();
43900                 },
43901                 tabIndex:-1
43902             });
43903             
43904             cmenu.menu.items.push({
43905                 actiontype : 'all',
43906                 html: 'Remove All Styles',
43907                 handler: function(a,b) {
43908                     
43909                     var c = Roo.get(editorcore.doc.body);
43910                     c.select('[style]').each(function(s) {
43911                         s.dom.removeAttribute('style');
43912                     });
43913                     editorcore.syncValue();
43914                 },
43915                 tabIndex:-1
43916             });
43917             
43918             cmenu.menu.items.push({
43919                 actiontype : 'all',
43920                 html: 'Remove All CSS Classes',
43921                 handler: function(a,b) {
43922                     
43923                     var c = Roo.get(editorcore.doc.body);
43924                     c.select('[class]').each(function(s) {
43925                         s.dom.className = '';
43926                     });
43927                     editorcore.syncValue();
43928                 },
43929                 tabIndex:-1
43930             });
43931             
43932              cmenu.menu.items.push({
43933                 actiontype : 'tidy',
43934                 html: 'Tidy HTML Source',
43935                 handler: function(a,b) {
43936                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43937                     editorcore.syncValue();
43938                 },
43939                 tabIndex:-1
43940             });
43941             
43942             
43943             tb.add(cmenu);
43944         }
43945          
43946         if (!this.disable.specialElements) {
43947             var semenu = {
43948                 text: "Other;",
43949                 cls: 'x-edit-none',
43950                 menu : {
43951                     items : []
43952                 }
43953             };
43954             for (var i =0; i < this.specialElements.length; i++) {
43955                 semenu.menu.items.push(
43956                     Roo.apply({ 
43957                         handler: function(a,b) {
43958                             editor.insertAtCursor(this.ihtml);
43959                         }
43960                     }, this.specialElements[i])
43961                 );
43962                     
43963             }
43964             
43965             tb.add(semenu);
43966             
43967             
43968         }
43969          
43970         
43971         if (this.btns) {
43972             for(var i =0; i< this.btns.length;i++) {
43973                 var b = Roo.factory(this.btns[i],Roo.form);
43974                 b.cls =  'x-edit-none';
43975                 
43976                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43977                     b.cls += ' x-init-enable';
43978                 }
43979                 
43980                 b.scope = editorcore;
43981                 tb.add(b);
43982             }
43983         
43984         }
43985         
43986         
43987         
43988         // disable everything...
43989         
43990         this.tb.items.each(function(item){
43991             
43992            if(
43993                 item.id != editorcore.frameId+ '-sourceedit' && 
43994                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43995             ){
43996                 
43997                 item.disable();
43998             }
43999         });
44000         this.rendered = true;
44001         
44002         // the all the btns;
44003         editor.on('editorevent', this.updateToolbar, this);
44004         // other toolbars need to implement this..
44005         //editor.on('editmodechange', this.updateToolbar, this);
44006     },
44007     
44008     
44009     relayBtnCmd : function(btn) {
44010         this.editorcore.relayCmd(btn.cmd);
44011     },
44012     // private used internally
44013     createLink : function(){
44014         Roo.log("create link?");
44015         var url = prompt(this.createLinkText, this.defaultLinkValue);
44016         if(url && url != 'http:/'+'/'){
44017             this.editorcore.relayCmd('createlink', url);
44018         }
44019     },
44020
44021     
44022     /**
44023      * Protected method that will not generally be called directly. It triggers
44024      * a toolbar update by reading the markup state of the current selection in the editor.
44025      */
44026     updateToolbar: function(){
44027
44028         if(!this.editorcore.activated){
44029             this.editor.onFirstFocus();
44030             return;
44031         }
44032
44033         var btns = this.tb.items.map, 
44034             doc = this.editorcore.doc,
44035             frameId = this.editorcore.frameId;
44036
44037         if(!this.disable.font && !Roo.isSafari){
44038             /*
44039             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
44040             if(name != this.fontSelect.dom.value){
44041                 this.fontSelect.dom.value = name;
44042             }
44043             */
44044         }
44045         if(!this.disable.format){
44046             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
44047             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
44048             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
44049             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
44050         }
44051         if(!this.disable.alignments){
44052             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
44053             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
44054             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
44055         }
44056         if(!Roo.isSafari && !this.disable.lists){
44057             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
44058             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
44059         }
44060         
44061         var ans = this.editorcore.getAllAncestors();
44062         if (this.formatCombo) {
44063             
44064             
44065             var store = this.formatCombo.store;
44066             this.formatCombo.setValue("");
44067             for (var i =0; i < ans.length;i++) {
44068                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
44069                     // select it..
44070                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
44071                     break;
44072                 }
44073             }
44074         }
44075         
44076         
44077         
44078         // hides menus... - so this cant be on a menu...
44079         Roo.menu.MenuMgr.hideAll();
44080
44081         //this.editorsyncValue();
44082     },
44083    
44084     
44085     createFontOptions : function(){
44086         var buf = [], fs = this.fontFamilies, ff, lc;
44087         
44088         
44089         
44090         for(var i = 0, len = fs.length; i< len; i++){
44091             ff = fs[i];
44092             lc = ff.toLowerCase();
44093             buf.push(
44094                 '<option value="',lc,'" style="font-family:',ff,';"',
44095                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44096                     ff,
44097                 '</option>'
44098             );
44099         }
44100         return buf.join('');
44101     },
44102     
44103     toggleSourceEdit : function(sourceEditMode){
44104         
44105         Roo.log("toolbar toogle");
44106         if(sourceEditMode === undefined){
44107             sourceEditMode = !this.sourceEditMode;
44108         }
44109         this.sourceEditMode = sourceEditMode === true;
44110         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44111         // just toggle the button?
44112         if(btn.pressed !== this.sourceEditMode){
44113             btn.toggle(this.sourceEditMode);
44114             return;
44115         }
44116         
44117         if(sourceEditMode){
44118             Roo.log("disabling buttons");
44119             this.tb.items.each(function(item){
44120                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44121                     item.disable();
44122                 }
44123             });
44124           
44125         }else{
44126             Roo.log("enabling buttons");
44127             if(this.editorcore.initialized){
44128                 this.tb.items.each(function(item){
44129                     item.enable();
44130                 });
44131             }
44132             
44133         }
44134         Roo.log("calling toggole on editor");
44135         // tell the editor that it's been pressed..
44136         this.editor.toggleSourceEdit(sourceEditMode);
44137        
44138     },
44139      /**
44140      * Object collection of toolbar tooltips for the buttons in the editor. The key
44141      * is the command id associated with that button and the value is a valid QuickTips object.
44142      * For example:
44143 <pre><code>
44144 {
44145     bold : {
44146         title: 'Bold (Ctrl+B)',
44147         text: 'Make the selected text bold.',
44148         cls: 'x-html-editor-tip'
44149     },
44150     italic : {
44151         title: 'Italic (Ctrl+I)',
44152         text: 'Make the selected text italic.',
44153         cls: 'x-html-editor-tip'
44154     },
44155     ...
44156 </code></pre>
44157     * @type Object
44158      */
44159     buttonTips : {
44160         bold : {
44161             title: 'Bold (Ctrl+B)',
44162             text: 'Make the selected text bold.',
44163             cls: 'x-html-editor-tip'
44164         },
44165         italic : {
44166             title: 'Italic (Ctrl+I)',
44167             text: 'Make the selected text italic.',
44168             cls: 'x-html-editor-tip'
44169         },
44170         underline : {
44171             title: 'Underline (Ctrl+U)',
44172             text: 'Underline the selected text.',
44173             cls: 'x-html-editor-tip'
44174         },
44175         strikethrough : {
44176             title: 'Strikethrough',
44177             text: 'Strikethrough the selected text.',
44178             cls: 'x-html-editor-tip'
44179         },
44180         increasefontsize : {
44181             title: 'Grow Text',
44182             text: 'Increase the font size.',
44183             cls: 'x-html-editor-tip'
44184         },
44185         decreasefontsize : {
44186             title: 'Shrink Text',
44187             text: 'Decrease the font size.',
44188             cls: 'x-html-editor-tip'
44189         },
44190         backcolor : {
44191             title: 'Text Highlight Color',
44192             text: 'Change the background color of the selected text.',
44193             cls: 'x-html-editor-tip'
44194         },
44195         forecolor : {
44196             title: 'Font Color',
44197             text: 'Change the color of the selected text.',
44198             cls: 'x-html-editor-tip'
44199         },
44200         justifyleft : {
44201             title: 'Align Text Left',
44202             text: 'Align text to the left.',
44203             cls: 'x-html-editor-tip'
44204         },
44205         justifycenter : {
44206             title: 'Center Text',
44207             text: 'Center text in the editor.',
44208             cls: 'x-html-editor-tip'
44209         },
44210         justifyright : {
44211             title: 'Align Text Right',
44212             text: 'Align text to the right.',
44213             cls: 'x-html-editor-tip'
44214         },
44215         insertunorderedlist : {
44216             title: 'Bullet List',
44217             text: 'Start a bulleted list.',
44218             cls: 'x-html-editor-tip'
44219         },
44220         insertorderedlist : {
44221             title: 'Numbered List',
44222             text: 'Start a numbered list.',
44223             cls: 'x-html-editor-tip'
44224         },
44225         createlink : {
44226             title: 'Hyperlink',
44227             text: 'Make the selected text a hyperlink.',
44228             cls: 'x-html-editor-tip'
44229         },
44230         sourceedit : {
44231             title: 'Source Edit',
44232             text: 'Switch to source editing mode.',
44233             cls: 'x-html-editor-tip'
44234         }
44235     },
44236     // private
44237     onDestroy : function(){
44238         if(this.rendered){
44239             
44240             this.tb.items.each(function(item){
44241                 if(item.menu){
44242                     item.menu.removeAll();
44243                     if(item.menu.el){
44244                         item.menu.el.destroy();
44245                     }
44246                 }
44247                 item.destroy();
44248             });
44249              
44250         }
44251     },
44252     onFirstFocus: function() {
44253         this.tb.items.each(function(item){
44254            item.enable();
44255         });
44256     }
44257 });
44258
44259
44260
44261
44262 // <script type="text/javascript">
44263 /*
44264  * Based on
44265  * Ext JS Library 1.1.1
44266  * Copyright(c) 2006-2007, Ext JS, LLC.
44267  *  
44268  
44269  */
44270
44271  
44272 /**
44273  * @class Roo.form.HtmlEditor.ToolbarContext
44274  * Context Toolbar
44275  * 
44276  * Usage:
44277  *
44278  new Roo.form.HtmlEditor({
44279     ....
44280     toolbars : [
44281         { xtype: 'ToolbarStandard', styles : {} }
44282         { xtype: 'ToolbarContext', disable : {} }
44283     ]
44284 })
44285
44286      
44287  * 
44288  * @config : {Object} disable List of elements to disable.. (not done yet.)
44289  * @config : {Object} styles  Map of styles available.
44290  * 
44291  */
44292
44293 Roo.form.HtmlEditor.ToolbarContext = function(config)
44294 {
44295     
44296     Roo.apply(this, config);
44297     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44298     // dont call parent... till later.
44299     this.styles = this.styles || {};
44300 }
44301
44302  
44303
44304 Roo.form.HtmlEditor.ToolbarContext.types = {
44305     'IMG' : {
44306         width : {
44307             title: "Width",
44308             width: 40
44309         },
44310         height:  {
44311             title: "Height",
44312             width: 40
44313         },
44314         align: {
44315             title: "Align",
44316             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44317             width : 80
44318             
44319         },
44320         border: {
44321             title: "Border",
44322             width: 40
44323         },
44324         alt: {
44325             title: "Alt",
44326             width: 120
44327         },
44328         src : {
44329             title: "Src",
44330             width: 220
44331         }
44332         
44333     },
44334     'A' : {
44335         name : {
44336             title: "Name",
44337             width: 50
44338         },
44339         target:  {
44340             title: "Target",
44341             width: 120
44342         },
44343         href:  {
44344             title: "Href",
44345             width: 220
44346         } // border?
44347         
44348     },
44349     'TABLE' : {
44350         rows : {
44351             title: "Rows",
44352             width: 20
44353         },
44354         cols : {
44355             title: "Cols",
44356             width: 20
44357         },
44358         width : {
44359             title: "Width",
44360             width: 40
44361         },
44362         height : {
44363             title: "Height",
44364             width: 40
44365         },
44366         border : {
44367             title: "Border",
44368             width: 20
44369         }
44370     },
44371     'TD' : {
44372         width : {
44373             title: "Width",
44374             width: 40
44375         },
44376         height : {
44377             title: "Height",
44378             width: 40
44379         },   
44380         align: {
44381             title: "Align",
44382             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44383             width: 80
44384         },
44385         valign: {
44386             title: "Valign",
44387             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44388             width: 80
44389         },
44390         colspan: {
44391             title: "Colspan",
44392             width: 20
44393             
44394         },
44395          'font-family'  : {
44396             title : "Font",
44397             style : 'fontFamily',
44398             displayField: 'display',
44399             optname : 'font-family',
44400             width: 140
44401         }
44402     },
44403     'INPUT' : {
44404         name : {
44405             title: "name",
44406             width: 120
44407         },
44408         value : {
44409             title: "Value",
44410             width: 120
44411         },
44412         width : {
44413             title: "Width",
44414             width: 40
44415         }
44416     },
44417     'LABEL' : {
44418         'for' : {
44419             title: "For",
44420             width: 120
44421         }
44422     },
44423     'TEXTAREA' : {
44424           name : {
44425             title: "name",
44426             width: 120
44427         },
44428         rows : {
44429             title: "Rows",
44430             width: 20
44431         },
44432         cols : {
44433             title: "Cols",
44434             width: 20
44435         }
44436     },
44437     'SELECT' : {
44438         name : {
44439             title: "name",
44440             width: 120
44441         },
44442         selectoptions : {
44443             title: "Options",
44444             width: 200
44445         }
44446     },
44447     
44448     // should we really allow this??
44449     // should this just be 
44450     'BODY' : {
44451         title : {
44452             title: "Title",
44453             width: 200,
44454             disabled : true
44455         }
44456     },
44457     'SPAN' : {
44458         'font-family'  : {
44459             title : "Font",
44460             style : 'fontFamily',
44461             displayField: 'display',
44462             optname : 'font-family',
44463             width: 140
44464         }
44465     },
44466     'DIV' : {
44467         'font-family'  : {
44468             title : "Font",
44469             style : 'fontFamily',
44470             displayField: 'display',
44471             optname : 'font-family',
44472             width: 140
44473         }
44474     },
44475      'P' : {
44476         'font-family'  : {
44477             title : "Font",
44478             style : 'fontFamily',
44479             displayField: 'display',
44480             optname : 'font-family',
44481             width: 140
44482         }
44483     },
44484     
44485     '*' : {
44486         // empty..
44487     }
44488
44489 };
44490
44491 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44492 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44493
44494 Roo.form.HtmlEditor.ToolbarContext.options = {
44495         'font-family'  : [ 
44496                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44497                 [ 'Courier New', 'Courier New'],
44498                 [ 'Tahoma', 'Tahoma'],
44499                 [ 'Times New Roman,serif', 'Times'],
44500                 [ 'Verdana','Verdana' ]
44501         ]
44502 };
44503
44504 // fixme - these need to be configurable..
44505  
44506
44507 //Roo.form.HtmlEditor.ToolbarContext.types
44508
44509
44510 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44511     
44512     tb: false,
44513     
44514     rendered: false,
44515     
44516     editor : false,
44517     editorcore : false,
44518     /**
44519      * @cfg {Object} disable  List of toolbar elements to disable
44520          
44521      */
44522     disable : false,
44523     /**
44524      * @cfg {Object} styles List of styles 
44525      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44526      *
44527      * These must be defined in the page, so they get rendered correctly..
44528      * .headline { }
44529      * TD.underline { }
44530      * 
44531      */
44532     styles : false,
44533     
44534     options: false,
44535     
44536     toolbars : false,
44537     
44538     init : function(editor)
44539     {
44540         this.editor = editor;
44541         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44542         var editorcore = this.editorcore;
44543         
44544         var fid = editorcore.frameId;
44545         var etb = this;
44546         function btn(id, toggle, handler){
44547             var xid = fid + '-'+ id ;
44548             return {
44549                 id : xid,
44550                 cmd : id,
44551                 cls : 'x-btn-icon x-edit-'+id,
44552                 enableToggle:toggle !== false,
44553                 scope: editorcore, // was editor...
44554                 handler:handler||editorcore.relayBtnCmd,
44555                 clickEvent:'mousedown',
44556                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44557                 tabIndex:-1
44558             };
44559         }
44560         // create a new element.
44561         var wdiv = editor.wrap.createChild({
44562                 tag: 'div'
44563             }, editor.wrap.dom.firstChild.nextSibling, true);
44564         
44565         // can we do this more than once??
44566         
44567          // stop form submits
44568       
44569  
44570         // disable everything...
44571         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44572         this.toolbars = {};
44573            
44574         for (var i in  ty) {
44575           
44576             this.toolbars[i] = this.buildToolbar(ty[i],i);
44577         }
44578         this.tb = this.toolbars.BODY;
44579         this.tb.el.show();
44580         this.buildFooter();
44581         this.footer.show();
44582         editor.on('hide', function( ) { this.footer.hide() }, this);
44583         editor.on('show', function( ) { this.footer.show() }, this);
44584         
44585          
44586         this.rendered = true;
44587         
44588         // the all the btns;
44589         editor.on('editorevent', this.updateToolbar, this);
44590         // other toolbars need to implement this..
44591         //editor.on('editmodechange', this.updateToolbar, this);
44592     },
44593     
44594     
44595     
44596     /**
44597      * Protected method that will not generally be called directly. It triggers
44598      * a toolbar update by reading the markup state of the current selection in the editor.
44599      *
44600      * Note you can force an update by calling on('editorevent', scope, false)
44601      */
44602     updateToolbar: function(editor,ev,sel){
44603
44604         //Roo.log(ev);
44605         // capture mouse up - this is handy for selecting images..
44606         // perhaps should go somewhere else...
44607         if(!this.editorcore.activated){
44608              this.editor.onFirstFocus();
44609             return;
44610         }
44611         
44612         
44613         
44614         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44615         // selectNode - might want to handle IE?
44616         if (ev &&
44617             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44618             ev.target && ev.target.tagName == 'IMG') {
44619             // they have click on an image...
44620             // let's see if we can change the selection...
44621             sel = ev.target;
44622          
44623               var nodeRange = sel.ownerDocument.createRange();
44624             try {
44625                 nodeRange.selectNode(sel);
44626             } catch (e) {
44627                 nodeRange.selectNodeContents(sel);
44628             }
44629             //nodeRange.collapse(true);
44630             var s = this.editorcore.win.getSelection();
44631             s.removeAllRanges();
44632             s.addRange(nodeRange);
44633         }  
44634         
44635       
44636         var updateFooter = sel ? false : true;
44637         
44638         
44639         var ans = this.editorcore.getAllAncestors();
44640         
44641         // pick
44642         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44643         
44644         if (!sel) { 
44645             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44646             sel = sel ? sel : this.editorcore.doc.body;
44647             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44648             
44649         }
44650         // pick a menu that exists..
44651         var tn = sel.tagName.toUpperCase();
44652         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44653         
44654         tn = sel.tagName.toUpperCase();
44655         
44656         var lastSel = this.tb.selectedNode;
44657         
44658         this.tb.selectedNode = sel;
44659         
44660         // if current menu does not match..
44661         
44662         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44663                 
44664             this.tb.el.hide();
44665             ///console.log("show: " + tn);
44666             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44667             this.tb.el.show();
44668             // update name
44669             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44670             
44671             
44672             // update attributes
44673             if (this.tb.fields) {
44674                 this.tb.fields.each(function(e) {
44675                     if (e.stylename) {
44676                         e.setValue(sel.style[e.stylename]);
44677                         return;
44678                     } 
44679                    e.setValue(sel.getAttribute(e.attrname));
44680                 });
44681             }
44682             
44683             var hasStyles = false;
44684             for(var i in this.styles) {
44685                 hasStyles = true;
44686                 break;
44687             }
44688             
44689             // update styles
44690             if (hasStyles) { 
44691                 var st = this.tb.fields.item(0);
44692                 
44693                 st.store.removeAll();
44694                
44695                 
44696                 var cn = sel.className.split(/\s+/);
44697                 
44698                 var avs = [];
44699                 if (this.styles['*']) {
44700                     
44701                     Roo.each(this.styles['*'], function(v) {
44702                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44703                     });
44704                 }
44705                 if (this.styles[tn]) { 
44706                     Roo.each(this.styles[tn], function(v) {
44707                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44708                     });
44709                 }
44710                 
44711                 st.store.loadData(avs);
44712                 st.collapse();
44713                 st.setValue(cn);
44714             }
44715             // flag our selected Node.
44716             this.tb.selectedNode = sel;
44717            
44718            
44719             Roo.menu.MenuMgr.hideAll();
44720
44721         }
44722         
44723         if (!updateFooter) {
44724             //this.footDisp.dom.innerHTML = ''; 
44725             return;
44726         }
44727         // update the footer
44728         //
44729         var html = '';
44730         
44731         this.footerEls = ans.reverse();
44732         Roo.each(this.footerEls, function(a,i) {
44733             if (!a) { return; }
44734             html += html.length ? ' &gt; '  :  '';
44735             
44736             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44737             
44738         });
44739        
44740         // 
44741         var sz = this.footDisp.up('td').getSize();
44742         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44743         this.footDisp.dom.style.marginLeft = '5px';
44744         
44745         this.footDisp.dom.style.overflow = 'hidden';
44746         
44747         this.footDisp.dom.innerHTML = html;
44748             
44749         //this.editorsyncValue();
44750     },
44751      
44752     
44753    
44754        
44755     // private
44756     onDestroy : function(){
44757         if(this.rendered){
44758             
44759             this.tb.items.each(function(item){
44760                 if(item.menu){
44761                     item.menu.removeAll();
44762                     if(item.menu.el){
44763                         item.menu.el.destroy();
44764                     }
44765                 }
44766                 item.destroy();
44767             });
44768              
44769         }
44770     },
44771     onFirstFocus: function() {
44772         // need to do this for all the toolbars..
44773         this.tb.items.each(function(item){
44774            item.enable();
44775         });
44776     },
44777     buildToolbar: function(tlist, nm)
44778     {
44779         var editor = this.editor;
44780         var editorcore = this.editorcore;
44781          // create a new element.
44782         var wdiv = editor.wrap.createChild({
44783                 tag: 'div'
44784             }, editor.wrap.dom.firstChild.nextSibling, true);
44785         
44786        
44787         var tb = new Roo.Toolbar(wdiv);
44788         // add the name..
44789         
44790         tb.add(nm+ ":&nbsp;");
44791         
44792         var styles = [];
44793         for(var i in this.styles) {
44794             styles.push(i);
44795         }
44796         
44797         // styles...
44798         if (styles && styles.length) {
44799             
44800             // this needs a multi-select checkbox...
44801             tb.addField( new Roo.form.ComboBox({
44802                 store: new Roo.data.SimpleStore({
44803                     id : 'val',
44804                     fields: ['val', 'selected'],
44805                     data : [] 
44806                 }),
44807                 name : '-roo-edit-className',
44808                 attrname : 'className',
44809                 displayField: 'val',
44810                 typeAhead: false,
44811                 mode: 'local',
44812                 editable : false,
44813                 triggerAction: 'all',
44814                 emptyText:'Select Style',
44815                 selectOnFocus:true,
44816                 width: 130,
44817                 listeners : {
44818                     'select': function(c, r, i) {
44819                         // initial support only for on class per el..
44820                         tb.selectedNode.className =  r ? r.get('val') : '';
44821                         editorcore.syncValue();
44822                     }
44823                 }
44824     
44825             }));
44826         }
44827         
44828         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44829         var tbops = tbc.options;
44830         
44831         for (var i in tlist) {
44832             
44833             var item = tlist[i];
44834             tb.add(item.title + ":&nbsp;");
44835             
44836             
44837             //optname == used so you can configure the options available..
44838             var opts = item.opts ? item.opts : false;
44839             if (item.optname) {
44840                 opts = tbops[item.optname];
44841            
44842             }
44843             
44844             if (opts) {
44845                 // opts == pulldown..
44846                 tb.addField( new Roo.form.ComboBox({
44847                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44848                         id : 'val',
44849                         fields: ['val', 'display'],
44850                         data : opts  
44851                     }),
44852                     name : '-roo-edit-' + i,
44853                     attrname : i,
44854                     stylename : item.style ? item.style : false,
44855                     displayField: item.displayField ? item.displayField : 'val',
44856                     valueField :  'val',
44857                     typeAhead: false,
44858                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44859                     editable : false,
44860                     triggerAction: 'all',
44861                     emptyText:'Select',
44862                     selectOnFocus:true,
44863                     width: item.width ? item.width  : 130,
44864                     listeners : {
44865                         'select': function(c, r, i) {
44866                             if (c.stylename) {
44867                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44868                                 return;
44869                             }
44870                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44871                         }
44872                     }
44873
44874                 }));
44875                 continue;
44876                     
44877                  
44878                 
44879                 tb.addField( new Roo.form.TextField({
44880                     name: i,
44881                     width: 100,
44882                     //allowBlank:false,
44883                     value: ''
44884                 }));
44885                 continue;
44886             }
44887             tb.addField( new Roo.form.TextField({
44888                 name: '-roo-edit-' + i,
44889                 attrname : i,
44890                 
44891                 width: item.width,
44892                 //allowBlank:true,
44893                 value: '',
44894                 listeners: {
44895                     'change' : function(f, nv, ov) {
44896                         tb.selectedNode.setAttribute(f.attrname, nv);
44897                         editorcore.syncValue();
44898                     }
44899                 }
44900             }));
44901              
44902         }
44903         
44904         var _this = this;
44905         
44906         if(nm == 'BODY'){
44907             tb.addSeparator();
44908         
44909             tb.addButton( {
44910                 text: 'Stylesheets',
44911
44912                 listeners : {
44913                     click : function ()
44914                     {
44915                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44916                     }
44917                 }
44918             });
44919         }
44920         
44921         tb.addFill();
44922         tb.addButton( {
44923             text: 'Remove Tag',
44924     
44925             listeners : {
44926                 click : function ()
44927                 {
44928                     // remove
44929                     // undo does not work.
44930                      
44931                     var sn = tb.selectedNode;
44932                     
44933                     var pn = sn.parentNode;
44934                     
44935                     var stn =  sn.childNodes[0];
44936                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44937                     while (sn.childNodes.length) {
44938                         var node = sn.childNodes[0];
44939                         sn.removeChild(node);
44940                         //Roo.log(node);
44941                         pn.insertBefore(node, sn);
44942                         
44943                     }
44944                     pn.removeChild(sn);
44945                     var range = editorcore.createRange();
44946         
44947                     range.setStart(stn,0);
44948                     range.setEnd(en,0); //????
44949                     //range.selectNode(sel);
44950                     
44951                     
44952                     var selection = editorcore.getSelection();
44953                     selection.removeAllRanges();
44954                     selection.addRange(range);
44955                     
44956                     
44957                     
44958                     //_this.updateToolbar(null, null, pn);
44959                     _this.updateToolbar(null, null, null);
44960                     _this.footDisp.dom.innerHTML = ''; 
44961                 }
44962             }
44963             
44964                     
44965                 
44966             
44967         });
44968         
44969         
44970         tb.el.on('click', function(e){
44971             e.preventDefault(); // what does this do?
44972         });
44973         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44974         tb.el.hide();
44975         tb.name = nm;
44976         // dont need to disable them... as they will get hidden
44977         return tb;
44978          
44979         
44980     },
44981     buildFooter : function()
44982     {
44983         
44984         var fel = this.editor.wrap.createChild();
44985         this.footer = new Roo.Toolbar(fel);
44986         // toolbar has scrolly on left / right?
44987         var footDisp= new Roo.Toolbar.Fill();
44988         var _t = this;
44989         this.footer.add(
44990             {
44991                 text : '&lt;',
44992                 xtype: 'Button',
44993                 handler : function() {
44994                     _t.footDisp.scrollTo('left',0,true)
44995                 }
44996             }
44997         );
44998         this.footer.add( footDisp );
44999         this.footer.add( 
45000             {
45001                 text : '&gt;',
45002                 xtype: 'Button',
45003                 handler : function() {
45004                     // no animation..
45005                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
45006                 }
45007             }
45008         );
45009         var fel = Roo.get(footDisp.el);
45010         fel.addClass('x-editor-context');
45011         this.footDispWrap = fel; 
45012         this.footDispWrap.overflow  = 'hidden';
45013         
45014         this.footDisp = fel.createChild();
45015         this.footDispWrap.on('click', this.onContextClick, this)
45016         
45017         
45018     },
45019     onContextClick : function (ev,dom)
45020     {
45021         ev.preventDefault();
45022         var  cn = dom.className;
45023         //Roo.log(cn);
45024         if (!cn.match(/x-ed-loc-/)) {
45025             return;
45026         }
45027         var n = cn.split('-').pop();
45028         var ans = this.footerEls;
45029         var sel = ans[n];
45030         
45031          // pick
45032         var range = this.editorcore.createRange();
45033         
45034         range.selectNodeContents(sel);
45035         //range.selectNode(sel);
45036         
45037         
45038         var selection = this.editorcore.getSelection();
45039         selection.removeAllRanges();
45040         selection.addRange(range);
45041         
45042         
45043         
45044         this.updateToolbar(null, null, sel);
45045         
45046         
45047     }
45048     
45049     
45050     
45051     
45052     
45053 });
45054
45055
45056
45057
45058
45059 /*
45060  * Based on:
45061  * Ext JS Library 1.1.1
45062  * Copyright(c) 2006-2007, Ext JS, LLC.
45063  *
45064  * Originally Released Under LGPL - original licence link has changed is not relivant.
45065  *
45066  * Fork - LGPL
45067  * <script type="text/javascript">
45068  */
45069  
45070 /**
45071  * @class Roo.form.BasicForm
45072  * @extends Roo.util.Observable
45073  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
45074  * @constructor
45075  * @param {String/HTMLElement/Roo.Element} el The form element or its id
45076  * @param {Object} config Configuration options
45077  */
45078 Roo.form.BasicForm = function(el, config){
45079     this.allItems = [];
45080     this.childForms = [];
45081     Roo.apply(this, config);
45082     /*
45083      * The Roo.form.Field items in this form.
45084      * @type MixedCollection
45085      */
45086      
45087      
45088     this.items = new Roo.util.MixedCollection(false, function(o){
45089         return o.id || (o.id = Roo.id());
45090     });
45091     this.addEvents({
45092         /**
45093          * @event beforeaction
45094          * Fires before any action is performed. Return false to cancel the action.
45095          * @param {Form} this
45096          * @param {Action} action The action to be performed
45097          */
45098         beforeaction: true,
45099         /**
45100          * @event actionfailed
45101          * Fires when an action fails.
45102          * @param {Form} this
45103          * @param {Action} action The action that failed
45104          */
45105         actionfailed : true,
45106         /**
45107          * @event actioncomplete
45108          * Fires when an action is completed.
45109          * @param {Form} this
45110          * @param {Action} action The action that completed
45111          */
45112         actioncomplete : true
45113     });
45114     if(el){
45115         this.initEl(el);
45116     }
45117     Roo.form.BasicForm.superclass.constructor.call(this);
45118 };
45119
45120 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45121     /**
45122      * @cfg {String} method
45123      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45124      */
45125     /**
45126      * @cfg {DataReader} reader
45127      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45128      * This is optional as there is built-in support for processing JSON.
45129      */
45130     /**
45131      * @cfg {DataReader} errorReader
45132      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45133      * This is completely optional as there is built-in support for processing JSON.
45134      */
45135     /**
45136      * @cfg {String} url
45137      * The URL to use for form actions if one isn't supplied in the action options.
45138      */
45139     /**
45140      * @cfg {Boolean} fileUpload
45141      * Set to true if this form is a file upload.
45142      */
45143      
45144     /**
45145      * @cfg {Object} baseParams
45146      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45147      */
45148      /**
45149      
45150     /**
45151      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45152      */
45153     timeout: 30,
45154
45155     // private
45156     activeAction : null,
45157
45158     /**
45159      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45160      * or setValues() data instead of when the form was first created.
45161      */
45162     trackResetOnLoad : false,
45163     
45164     
45165     /**
45166      * childForms - used for multi-tab forms
45167      * @type {Array}
45168      */
45169     childForms : false,
45170     
45171     /**
45172      * allItems - full list of fields.
45173      * @type {Array}
45174      */
45175     allItems : false,
45176     
45177     /**
45178      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45179      * element by passing it or its id or mask the form itself by passing in true.
45180      * @type Mixed
45181      */
45182     waitMsgTarget : false,
45183
45184     // private
45185     initEl : function(el){
45186         this.el = Roo.get(el);
45187         this.id = this.el.id || Roo.id();
45188         this.el.on('submit', this.onSubmit, this);
45189         this.el.addClass('x-form');
45190     },
45191
45192     // private
45193     onSubmit : function(e){
45194         e.stopEvent();
45195     },
45196
45197     /**
45198      * Returns true if client-side validation on the form is successful.
45199      * @return Boolean
45200      */
45201     isValid : function(){
45202         var valid = true;
45203         this.items.each(function(f){
45204            if(!f.validate()){
45205                valid = false;
45206            }
45207         });
45208         return valid;
45209     },
45210
45211     /**
45212      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
45213      * @return Boolean
45214      */
45215     isDirty : function(){
45216         var dirty = false;
45217         this.items.each(function(f){
45218            if(f.isDirty()){
45219                dirty = true;
45220                return false;
45221            }
45222         });
45223         return dirty;
45224     },
45225     
45226     /**
45227      * Returns true if any fields in this form have changed since their original load. (New version)
45228      * @return Boolean
45229      */
45230     
45231     hasChanged : function()
45232     {
45233         var dirty = false;
45234         this.items.each(function(f){
45235            if(f.hasChanged()){
45236                dirty = true;
45237                return false;
45238            }
45239         });
45240         return dirty;
45241         
45242     },
45243     /**
45244      * Resets all hasChanged to 'false' -
45245      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
45246      * So hasChanged storage is only to be used for this purpose
45247      * @return Boolean
45248      */
45249     resetHasChanged : function()
45250     {
45251         this.items.each(function(f){
45252            f.resetHasChanged();
45253         });
45254         
45255     },
45256     
45257     
45258     /**
45259      * Performs a predefined action (submit or load) or custom actions you define on this form.
45260      * @param {String} actionName The name of the action type
45261      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45262      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45263      * accept other config options):
45264      * <pre>
45265 Property          Type             Description
45266 ----------------  ---------------  ----------------------------------------------------------------------------------
45267 url               String           The url for the action (defaults to the form's url)
45268 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45269 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45270 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45271                                    validate the form on the client (defaults to false)
45272      * </pre>
45273      * @return {BasicForm} this
45274      */
45275     doAction : function(action, options){
45276         if(typeof action == 'string'){
45277             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45278         }
45279         if(this.fireEvent('beforeaction', this, action) !== false){
45280             this.beforeAction(action);
45281             action.run.defer(100, action);
45282         }
45283         return this;
45284     },
45285
45286     /**
45287      * Shortcut to do a submit action.
45288      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45289      * @return {BasicForm} this
45290      */
45291     submit : function(options){
45292         this.doAction('submit', options);
45293         return this;
45294     },
45295
45296     /**
45297      * Shortcut to do a load action.
45298      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45299      * @return {BasicForm} this
45300      */
45301     load : function(options){
45302         this.doAction('load', options);
45303         return this;
45304     },
45305
45306     /**
45307      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45308      * @param {Record} record The record to edit
45309      * @return {BasicForm} this
45310      */
45311     updateRecord : function(record){
45312         record.beginEdit();
45313         var fs = record.fields;
45314         fs.each(function(f){
45315             var field = this.findField(f.name);
45316             if(field){
45317                 record.set(f.name, field.getValue());
45318             }
45319         }, this);
45320         record.endEdit();
45321         return this;
45322     },
45323
45324     /**
45325      * Loads an Roo.data.Record into this form.
45326      * @param {Record} record The record to load
45327      * @return {BasicForm} this
45328      */
45329     loadRecord : function(record){
45330         this.setValues(record.data);
45331         return this;
45332     },
45333
45334     // private
45335     beforeAction : function(action){
45336         var o = action.options;
45337         
45338        
45339         if(this.waitMsgTarget === true){
45340             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45341         }else if(this.waitMsgTarget){
45342             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45343             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45344         }else {
45345             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45346         }
45347          
45348     },
45349
45350     // private
45351     afterAction : function(action, success){
45352         this.activeAction = null;
45353         var o = action.options;
45354         
45355         if(this.waitMsgTarget === true){
45356             this.el.unmask();
45357         }else if(this.waitMsgTarget){
45358             this.waitMsgTarget.unmask();
45359         }else{
45360             Roo.MessageBox.updateProgress(1);
45361             Roo.MessageBox.hide();
45362         }
45363          
45364         if(success){
45365             if(o.reset){
45366                 this.reset();
45367             }
45368             Roo.callback(o.success, o.scope, [this, action]);
45369             this.fireEvent('actioncomplete', this, action);
45370             
45371         }else{
45372             
45373             // failure condition..
45374             // we have a scenario where updates need confirming.
45375             // eg. if a locking scenario exists..
45376             // we look for { errors : { needs_confirm : true }} in the response.
45377             if (
45378                 (typeof(action.result) != 'undefined')  &&
45379                 (typeof(action.result.errors) != 'undefined')  &&
45380                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45381            ){
45382                 var _t = this;
45383                 Roo.MessageBox.confirm(
45384                     "Change requires confirmation",
45385                     action.result.errorMsg,
45386                     function(r) {
45387                         if (r != 'yes') {
45388                             return;
45389                         }
45390                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45391                     }
45392                     
45393                 );
45394                 
45395                 
45396                 
45397                 return;
45398             }
45399             
45400             Roo.callback(o.failure, o.scope, [this, action]);
45401             // show an error message if no failed handler is set..
45402             if (!this.hasListener('actionfailed')) {
45403                 Roo.MessageBox.alert("Error",
45404                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45405                         action.result.errorMsg :
45406                         "Saving Failed, please check your entries or try again"
45407                 );
45408             }
45409             
45410             this.fireEvent('actionfailed', this, action);
45411         }
45412         
45413     },
45414
45415     /**
45416      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45417      * @param {String} id The value to search for
45418      * @return Field
45419      */
45420     findField : function(id){
45421         var field = this.items.get(id);
45422         if(!field){
45423             this.items.each(function(f){
45424                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45425                     field = f;
45426                     return false;
45427                 }
45428             });
45429         }
45430         return field || null;
45431     },
45432
45433     /**
45434      * Add a secondary form to this one, 
45435      * Used to provide tabbed forms. One form is primary, with hidden values 
45436      * which mirror the elements from the other forms.
45437      * 
45438      * @param {Roo.form.Form} form to add.
45439      * 
45440      */
45441     addForm : function(form)
45442     {
45443        
45444         if (this.childForms.indexOf(form) > -1) {
45445             // already added..
45446             return;
45447         }
45448         this.childForms.push(form);
45449         var n = '';
45450         Roo.each(form.allItems, function (fe) {
45451             
45452             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45453             if (this.findField(n)) { // already added..
45454                 return;
45455             }
45456             var add = new Roo.form.Hidden({
45457                 name : n
45458             });
45459             add.render(this.el);
45460             
45461             this.add( add );
45462         }, this);
45463         
45464     },
45465     /**
45466      * Mark fields in this form invalid in bulk.
45467      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45468      * @return {BasicForm} this
45469      */
45470     markInvalid : function(errors){
45471         if(errors instanceof Array){
45472             for(var i = 0, len = errors.length; i < len; i++){
45473                 var fieldError = errors[i];
45474                 var f = this.findField(fieldError.id);
45475                 if(f){
45476                     f.markInvalid(fieldError.msg);
45477                 }
45478             }
45479         }else{
45480             var field, id;
45481             for(id in errors){
45482                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45483                     field.markInvalid(errors[id]);
45484                 }
45485             }
45486         }
45487         Roo.each(this.childForms || [], function (f) {
45488             f.markInvalid(errors);
45489         });
45490         
45491         return this;
45492     },
45493
45494     /**
45495      * Set values for fields in this form in bulk.
45496      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45497      * @return {BasicForm} this
45498      */
45499     setValues : function(values){
45500         if(values instanceof Array){ // array of objects
45501             for(var i = 0, len = values.length; i < len; i++){
45502                 var v = values[i];
45503                 var f = this.findField(v.id);
45504                 if(f){
45505                     f.setValue(v.value);
45506                     if(this.trackResetOnLoad){
45507                         f.originalValue = f.getValue();
45508                     }
45509                 }
45510             }
45511         }else{ // object hash
45512             var field, id;
45513             for(id in values){
45514                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45515                     
45516                     if (field.setFromData && 
45517                         field.valueField && 
45518                         field.displayField &&
45519                         // combos' with local stores can 
45520                         // be queried via setValue()
45521                         // to set their value..
45522                         (field.store && !field.store.isLocal)
45523                         ) {
45524                         // it's a combo
45525                         var sd = { };
45526                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45527                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45528                         field.setFromData(sd);
45529                         
45530                     } else {
45531                         field.setValue(values[id]);
45532                     }
45533                     
45534                     
45535                     if(this.trackResetOnLoad){
45536                         field.originalValue = field.getValue();
45537                     }
45538                 }
45539             }
45540         }
45541         this.resetHasChanged();
45542         
45543         
45544         Roo.each(this.childForms || [], function (f) {
45545             f.setValues(values);
45546             f.resetHasChanged();
45547         });
45548                 
45549         return this;
45550     },
45551
45552     /**
45553      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45554      * they are returned as an array.
45555      * @param {Boolean} asString
45556      * @return {Object}
45557      */
45558     getValues : function(asString){
45559         if (this.childForms) {
45560             // copy values from the child forms
45561             Roo.each(this.childForms, function (f) {
45562                 this.setValues(f.getValues());
45563             }, this);
45564         }
45565         
45566         
45567         
45568         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45569         if(asString === true){
45570             return fs;
45571         }
45572         return Roo.urlDecode(fs);
45573     },
45574     
45575     /**
45576      * Returns the fields in this form as an object with key/value pairs. 
45577      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45578      * @return {Object}
45579      */
45580     getFieldValues : function(with_hidden)
45581     {
45582         if (this.childForms) {
45583             // copy values from the child forms
45584             // should this call getFieldValues - probably not as we do not currently copy
45585             // hidden fields when we generate..
45586             Roo.each(this.childForms, function (f) {
45587                 this.setValues(f.getValues());
45588             }, this);
45589         }
45590         
45591         var ret = {};
45592         this.items.each(function(f){
45593             if (!f.getName()) {
45594                 return;
45595             }
45596             var v = f.getValue();
45597             if (f.inputType =='radio') {
45598                 if (typeof(ret[f.getName()]) == 'undefined') {
45599                     ret[f.getName()] = ''; // empty..
45600                 }
45601                 
45602                 if (!f.el.dom.checked) {
45603                     return;
45604                     
45605                 }
45606                 v = f.el.dom.value;
45607                 
45608             }
45609             
45610             // not sure if this supported any more..
45611             if ((typeof(v) == 'object') && f.getRawValue) {
45612                 v = f.getRawValue() ; // dates..
45613             }
45614             // combo boxes where name != hiddenName...
45615             if (f.name != f.getName()) {
45616                 ret[f.name] = f.getRawValue();
45617             }
45618             ret[f.getName()] = v;
45619         });
45620         
45621         return ret;
45622     },
45623
45624     /**
45625      * Clears all invalid messages in this form.
45626      * @return {BasicForm} this
45627      */
45628     clearInvalid : function(){
45629         this.items.each(function(f){
45630            f.clearInvalid();
45631         });
45632         
45633         Roo.each(this.childForms || [], function (f) {
45634             f.clearInvalid();
45635         });
45636         
45637         
45638         return this;
45639     },
45640
45641     /**
45642      * Resets this form.
45643      * @return {BasicForm} this
45644      */
45645     reset : function(){
45646         this.items.each(function(f){
45647             f.reset();
45648         });
45649         
45650         Roo.each(this.childForms || [], function (f) {
45651             f.reset();
45652         });
45653         this.resetHasChanged();
45654         
45655         return this;
45656     },
45657
45658     /**
45659      * Add Roo.form components to this form.
45660      * @param {Field} field1
45661      * @param {Field} field2 (optional)
45662      * @param {Field} etc (optional)
45663      * @return {BasicForm} this
45664      */
45665     add : function(){
45666         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45667         return this;
45668     },
45669
45670
45671     /**
45672      * Removes a field from the items collection (does NOT remove its markup).
45673      * @param {Field} field
45674      * @return {BasicForm} this
45675      */
45676     remove : function(field){
45677         this.items.remove(field);
45678         return this;
45679     },
45680
45681     /**
45682      * Looks at the fields in this form, checks them for an id attribute,
45683      * and calls applyTo on the existing dom element with that id.
45684      * @return {BasicForm} this
45685      */
45686     render : function(){
45687         this.items.each(function(f){
45688             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45689                 f.applyTo(f.id);
45690             }
45691         });
45692         return this;
45693     },
45694
45695     /**
45696      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45697      * @param {Object} values
45698      * @return {BasicForm} this
45699      */
45700     applyToFields : function(o){
45701         this.items.each(function(f){
45702            Roo.apply(f, o);
45703         });
45704         return this;
45705     },
45706
45707     /**
45708      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45709      * @param {Object} values
45710      * @return {BasicForm} this
45711      */
45712     applyIfToFields : function(o){
45713         this.items.each(function(f){
45714            Roo.applyIf(f, o);
45715         });
45716         return this;
45717     }
45718 });
45719
45720 // back compat
45721 Roo.BasicForm = Roo.form.BasicForm;/*
45722  * Based on:
45723  * Ext JS Library 1.1.1
45724  * Copyright(c) 2006-2007, Ext JS, LLC.
45725  *
45726  * Originally Released Under LGPL - original licence link has changed is not relivant.
45727  *
45728  * Fork - LGPL
45729  * <script type="text/javascript">
45730  */
45731
45732 /**
45733  * @class Roo.form.Form
45734  * @extends Roo.form.BasicForm
45735  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45736  * @constructor
45737  * @param {Object} config Configuration options
45738  */
45739 Roo.form.Form = function(config){
45740     var xitems =  [];
45741     if (config.items) {
45742         xitems = config.items;
45743         delete config.items;
45744     }
45745    
45746     
45747     Roo.form.Form.superclass.constructor.call(this, null, config);
45748     this.url = this.url || this.action;
45749     if(!this.root){
45750         this.root = new Roo.form.Layout(Roo.applyIf({
45751             id: Roo.id()
45752         }, config));
45753     }
45754     this.active = this.root;
45755     /**
45756      * Array of all the buttons that have been added to this form via {@link addButton}
45757      * @type Array
45758      */
45759     this.buttons = [];
45760     this.allItems = [];
45761     this.addEvents({
45762         /**
45763          * @event clientvalidation
45764          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45765          * @param {Form} this
45766          * @param {Boolean} valid true if the form has passed client-side validation
45767          */
45768         clientvalidation: true,
45769         /**
45770          * @event rendered
45771          * Fires when the form is rendered
45772          * @param {Roo.form.Form} form
45773          */
45774         rendered : true
45775     });
45776     
45777     if (this.progressUrl) {
45778             // push a hidden field onto the list of fields..
45779             this.addxtype( {
45780                     xns: Roo.form, 
45781                     xtype : 'Hidden', 
45782                     name : 'UPLOAD_IDENTIFIER' 
45783             });
45784         }
45785         
45786     
45787     Roo.each(xitems, this.addxtype, this);
45788     
45789     
45790     
45791 };
45792
45793 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45794     /**
45795      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45796      */
45797     /**
45798      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45799      */
45800     /**
45801      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45802      */
45803     buttonAlign:'center',
45804
45805     /**
45806      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45807      */
45808     minButtonWidth:75,
45809
45810     /**
45811      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45812      * This property cascades to child containers if not set.
45813      */
45814     labelAlign:'left',
45815
45816     /**
45817      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45818      * fires a looping event with that state. This is required to bind buttons to the valid
45819      * state using the config value formBind:true on the button.
45820      */
45821     monitorValid : false,
45822
45823     /**
45824      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45825      */
45826     monitorPoll : 200,
45827     
45828     /**
45829      * @cfg {String} progressUrl - Url to return progress data 
45830      */
45831     
45832     progressUrl : false,
45833   
45834     /**
45835      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45836      * fields are added and the column is closed. If no fields are passed the column remains open
45837      * until end() is called.
45838      * @param {Object} config The config to pass to the column
45839      * @param {Field} field1 (optional)
45840      * @param {Field} field2 (optional)
45841      * @param {Field} etc (optional)
45842      * @return Column The column container object
45843      */
45844     column : function(c){
45845         var col = new Roo.form.Column(c);
45846         this.start(col);
45847         if(arguments.length > 1){ // duplicate code required because of Opera
45848             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45849             this.end();
45850         }
45851         return col;
45852     },
45853
45854     /**
45855      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45856      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45857      * until end() is called.
45858      * @param {Object} config The config to pass to the fieldset
45859      * @param {Field} field1 (optional)
45860      * @param {Field} field2 (optional)
45861      * @param {Field} etc (optional)
45862      * @return FieldSet The fieldset container object
45863      */
45864     fieldset : function(c){
45865         var fs = new Roo.form.FieldSet(c);
45866         this.start(fs);
45867         if(arguments.length > 1){ // duplicate code required because of Opera
45868             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45869             this.end();
45870         }
45871         return fs;
45872     },
45873
45874     /**
45875      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45876      * fields are added and the container is closed. If no fields are passed the container remains open
45877      * until end() is called.
45878      * @param {Object} config The config to pass to the Layout
45879      * @param {Field} field1 (optional)
45880      * @param {Field} field2 (optional)
45881      * @param {Field} etc (optional)
45882      * @return Layout The container object
45883      */
45884     container : function(c){
45885         var l = new Roo.form.Layout(c);
45886         this.start(l);
45887         if(arguments.length > 1){ // duplicate code required because of Opera
45888             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45889             this.end();
45890         }
45891         return l;
45892     },
45893
45894     /**
45895      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45896      * @param {Object} container A Roo.form.Layout or subclass of Layout
45897      * @return {Form} this
45898      */
45899     start : function(c){
45900         // cascade label info
45901         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45902         this.active.stack.push(c);
45903         c.ownerCt = this.active;
45904         this.active = c;
45905         return this;
45906     },
45907
45908     /**
45909      * Closes the current open container
45910      * @return {Form} this
45911      */
45912     end : function(){
45913         if(this.active == this.root){
45914             return this;
45915         }
45916         this.active = this.active.ownerCt;
45917         return this;
45918     },
45919
45920     /**
45921      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45922      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45923      * as the label of the field.
45924      * @param {Field} field1
45925      * @param {Field} field2 (optional)
45926      * @param {Field} etc. (optional)
45927      * @return {Form} this
45928      */
45929     add : function(){
45930         this.active.stack.push.apply(this.active.stack, arguments);
45931         this.allItems.push.apply(this.allItems,arguments);
45932         var r = [];
45933         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45934             if(a[i].isFormField){
45935                 r.push(a[i]);
45936             }
45937         }
45938         if(r.length > 0){
45939             Roo.form.Form.superclass.add.apply(this, r);
45940         }
45941         return this;
45942     },
45943     
45944
45945     
45946     
45947     
45948      /**
45949      * Find any element that has been added to a form, using it's ID or name
45950      * This can include framesets, columns etc. along with regular fields..
45951      * @param {String} id - id or name to find.
45952      
45953      * @return {Element} e - or false if nothing found.
45954      */
45955     findbyId : function(id)
45956     {
45957         var ret = false;
45958         if (!id) {
45959             return ret;
45960         }
45961         Roo.each(this.allItems, function(f){
45962             if (f.id == id || f.name == id ){
45963                 ret = f;
45964                 return false;
45965             }
45966         });
45967         return ret;
45968     },
45969
45970     
45971     
45972     /**
45973      * Render this form into the passed container. This should only be called once!
45974      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45975      * @return {Form} this
45976      */
45977     render : function(ct)
45978     {
45979         
45980         
45981         
45982         ct = Roo.get(ct);
45983         var o = this.autoCreate || {
45984             tag: 'form',
45985             method : this.method || 'POST',
45986             id : this.id || Roo.id()
45987         };
45988         this.initEl(ct.createChild(o));
45989
45990         this.root.render(this.el);
45991         
45992        
45993              
45994         this.items.each(function(f){
45995             f.render('x-form-el-'+f.id);
45996         });
45997
45998         if(this.buttons.length > 0){
45999             // tables are required to maintain order and for correct IE layout
46000             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
46001                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
46002                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
46003             }}, null, true);
46004             var tr = tb.getElementsByTagName('tr')[0];
46005             for(var i = 0, len = this.buttons.length; i < len; i++) {
46006                 var b = this.buttons[i];
46007                 var td = document.createElement('td');
46008                 td.className = 'x-form-btn-td';
46009                 b.render(tr.appendChild(td));
46010             }
46011         }
46012         if(this.monitorValid){ // initialize after render
46013             this.startMonitoring();
46014         }
46015         this.fireEvent('rendered', this);
46016         return this;
46017     },
46018
46019     /**
46020      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
46021      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
46022      * object or a valid Roo.DomHelper element config
46023      * @param {Function} handler The function called when the button is clicked
46024      * @param {Object} scope (optional) The scope of the handler function
46025      * @return {Roo.Button}
46026      */
46027     addButton : function(config, handler, scope){
46028         var bc = {
46029             handler: handler,
46030             scope: scope,
46031             minWidth: this.minButtonWidth,
46032             hideParent:true
46033         };
46034         if(typeof config == "string"){
46035             bc.text = config;
46036         }else{
46037             Roo.apply(bc, config);
46038         }
46039         var btn = new Roo.Button(null, bc);
46040         this.buttons.push(btn);
46041         return btn;
46042     },
46043
46044      /**
46045      * Adds a series of form elements (using the xtype property as the factory method.
46046      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
46047      * @param {Object} config 
46048      */
46049     
46050     addxtype : function()
46051     {
46052         var ar = Array.prototype.slice.call(arguments, 0);
46053         var ret = false;
46054         for(var i = 0; i < ar.length; i++) {
46055             if (!ar[i]) {
46056                 continue; // skip -- if this happends something invalid got sent, we 
46057                 // should ignore it, as basically that interface element will not show up
46058                 // and that should be pretty obvious!!
46059             }
46060             
46061             if (Roo.form[ar[i].xtype]) {
46062                 ar[i].form = this;
46063                 var fe = Roo.factory(ar[i], Roo.form);
46064                 if (!ret) {
46065                     ret = fe;
46066                 }
46067                 fe.form = this;
46068                 if (fe.store) {
46069                     fe.store.form = this;
46070                 }
46071                 if (fe.isLayout) {  
46072                          
46073                     this.start(fe);
46074                     this.allItems.push(fe);
46075                     if (fe.items && fe.addxtype) {
46076                         fe.addxtype.apply(fe, fe.items);
46077                         delete fe.items;
46078                     }
46079                      this.end();
46080                     continue;
46081                 }
46082                 
46083                 
46084                  
46085                 this.add(fe);
46086               //  console.log('adding ' + ar[i].xtype);
46087             }
46088             if (ar[i].xtype == 'Button') {  
46089                 //console.log('adding button');
46090                 //console.log(ar[i]);
46091                 this.addButton(ar[i]);
46092                 this.allItems.push(fe);
46093                 continue;
46094             }
46095             
46096             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
46097                 alert('end is not supported on xtype any more, use items');
46098             //    this.end();
46099             //    //console.log('adding end');
46100             }
46101             
46102         }
46103         return ret;
46104     },
46105     
46106     /**
46107      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
46108      * option "monitorValid"
46109      */
46110     startMonitoring : function(){
46111         if(!this.bound){
46112             this.bound = true;
46113             Roo.TaskMgr.start({
46114                 run : this.bindHandler,
46115                 interval : this.monitorPoll || 200,
46116                 scope: this
46117             });
46118         }
46119     },
46120
46121     /**
46122      * Stops monitoring of the valid state of this form
46123      */
46124     stopMonitoring : function(){
46125         this.bound = false;
46126     },
46127
46128     // private
46129     bindHandler : function(){
46130         if(!this.bound){
46131             return false; // stops binding
46132         }
46133         var valid = true;
46134         this.items.each(function(f){
46135             if(!f.isValid(true)){
46136                 valid = false;
46137                 return false;
46138             }
46139         });
46140         for(var i = 0, len = this.buttons.length; i < len; i++){
46141             var btn = this.buttons[i];
46142             if(btn.formBind === true && btn.disabled === valid){
46143                 btn.setDisabled(!valid);
46144             }
46145         }
46146         this.fireEvent('clientvalidation', this, valid);
46147     }
46148     
46149     
46150     
46151     
46152     
46153     
46154     
46155     
46156 });
46157
46158
46159 // back compat
46160 Roo.Form = Roo.form.Form;
46161 /*
46162  * Based on:
46163  * Ext JS Library 1.1.1
46164  * Copyright(c) 2006-2007, Ext JS, LLC.
46165  *
46166  * Originally Released Under LGPL - original licence link has changed is not relivant.
46167  *
46168  * Fork - LGPL
46169  * <script type="text/javascript">
46170  */
46171
46172 // as we use this in bootstrap.
46173 Roo.namespace('Roo.form');
46174  /**
46175  * @class Roo.form.Action
46176  * Internal Class used to handle form actions
46177  * @constructor
46178  * @param {Roo.form.BasicForm} el The form element or its id
46179  * @param {Object} config Configuration options
46180  */
46181
46182  
46183  
46184 // define the action interface
46185 Roo.form.Action = function(form, options){
46186     this.form = form;
46187     this.options = options || {};
46188 };
46189 /**
46190  * Client Validation Failed
46191  * @const 
46192  */
46193 Roo.form.Action.CLIENT_INVALID = 'client';
46194 /**
46195  * Server Validation Failed
46196  * @const 
46197  */
46198 Roo.form.Action.SERVER_INVALID = 'server';
46199  /**
46200  * Connect to Server Failed
46201  * @const 
46202  */
46203 Roo.form.Action.CONNECT_FAILURE = 'connect';
46204 /**
46205  * Reading Data from Server Failed
46206  * @const 
46207  */
46208 Roo.form.Action.LOAD_FAILURE = 'load';
46209
46210 Roo.form.Action.prototype = {
46211     type : 'default',
46212     failureType : undefined,
46213     response : undefined,
46214     result : undefined,
46215
46216     // interface method
46217     run : function(options){
46218
46219     },
46220
46221     // interface method
46222     success : function(response){
46223
46224     },
46225
46226     // interface method
46227     handleResponse : function(response){
46228
46229     },
46230
46231     // default connection failure
46232     failure : function(response){
46233         
46234         this.response = response;
46235         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46236         this.form.afterAction(this, false);
46237     },
46238
46239     processResponse : function(response){
46240         this.response = response;
46241         if(!response.responseText){
46242             return true;
46243         }
46244         this.result = this.handleResponse(response);
46245         return this.result;
46246     },
46247
46248     // utility functions used internally
46249     getUrl : function(appendParams){
46250         var url = this.options.url || this.form.url || this.form.el.dom.action;
46251         if(appendParams){
46252             var p = this.getParams();
46253             if(p){
46254                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46255             }
46256         }
46257         return url;
46258     },
46259
46260     getMethod : function(){
46261         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46262     },
46263
46264     getParams : function(){
46265         var bp = this.form.baseParams;
46266         var p = this.options.params;
46267         if(p){
46268             if(typeof p == "object"){
46269                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46270             }else if(typeof p == 'string' && bp){
46271                 p += '&' + Roo.urlEncode(bp);
46272             }
46273         }else if(bp){
46274             p = Roo.urlEncode(bp);
46275         }
46276         return p;
46277     },
46278
46279     createCallback : function(){
46280         return {
46281             success: this.success,
46282             failure: this.failure,
46283             scope: this,
46284             timeout: (this.form.timeout*1000),
46285             upload: this.form.fileUpload ? this.success : undefined
46286         };
46287     }
46288 };
46289
46290 Roo.form.Action.Submit = function(form, options){
46291     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46292 };
46293
46294 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46295     type : 'submit',
46296
46297     haveProgress : false,
46298     uploadComplete : false,
46299     
46300     // uploadProgress indicator.
46301     uploadProgress : function()
46302     {
46303         if (!this.form.progressUrl) {
46304             return;
46305         }
46306         
46307         if (!this.haveProgress) {
46308             Roo.MessageBox.progress("Uploading", "Uploading");
46309         }
46310         if (this.uploadComplete) {
46311            Roo.MessageBox.hide();
46312            return;
46313         }
46314         
46315         this.haveProgress = true;
46316    
46317         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46318         
46319         var c = new Roo.data.Connection();
46320         c.request({
46321             url : this.form.progressUrl,
46322             params: {
46323                 id : uid
46324             },
46325             method: 'GET',
46326             success : function(req){
46327                //console.log(data);
46328                 var rdata = false;
46329                 var edata;
46330                 try  {
46331                    rdata = Roo.decode(req.responseText)
46332                 } catch (e) {
46333                     Roo.log("Invalid data from server..");
46334                     Roo.log(edata);
46335                     return;
46336                 }
46337                 if (!rdata || !rdata.success) {
46338                     Roo.log(rdata);
46339                     Roo.MessageBox.alert(Roo.encode(rdata));
46340                     return;
46341                 }
46342                 var data = rdata.data;
46343                 
46344                 if (this.uploadComplete) {
46345                    Roo.MessageBox.hide();
46346                    return;
46347                 }
46348                    
46349                 if (data){
46350                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46351                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46352                     );
46353                 }
46354                 this.uploadProgress.defer(2000,this);
46355             },
46356        
46357             failure: function(data) {
46358                 Roo.log('progress url failed ');
46359                 Roo.log(data);
46360             },
46361             scope : this
46362         });
46363            
46364     },
46365     
46366     
46367     run : function()
46368     {
46369         // run get Values on the form, so it syncs any secondary forms.
46370         this.form.getValues();
46371         
46372         var o = this.options;
46373         var method = this.getMethod();
46374         var isPost = method == 'POST';
46375         if(o.clientValidation === false || this.form.isValid()){
46376             
46377             if (this.form.progressUrl) {
46378                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46379                     (new Date() * 1) + '' + Math.random());
46380                     
46381             } 
46382             
46383             
46384             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46385                 form:this.form.el.dom,
46386                 url:this.getUrl(!isPost),
46387                 method: method,
46388                 params:isPost ? this.getParams() : null,
46389                 isUpload: this.form.fileUpload
46390             }));
46391             
46392             this.uploadProgress();
46393
46394         }else if (o.clientValidation !== false){ // client validation failed
46395             this.failureType = Roo.form.Action.CLIENT_INVALID;
46396             this.form.afterAction(this, false);
46397         }
46398     },
46399
46400     success : function(response)
46401     {
46402         this.uploadComplete= true;
46403         if (this.haveProgress) {
46404             Roo.MessageBox.hide();
46405         }
46406         
46407         
46408         var result = this.processResponse(response);
46409         if(result === true || result.success){
46410             this.form.afterAction(this, true);
46411             return;
46412         }
46413         if(result.errors){
46414             this.form.markInvalid(result.errors);
46415             this.failureType = Roo.form.Action.SERVER_INVALID;
46416         }
46417         this.form.afterAction(this, false);
46418     },
46419     failure : function(response)
46420     {
46421         this.uploadComplete= true;
46422         if (this.haveProgress) {
46423             Roo.MessageBox.hide();
46424         }
46425         
46426         this.response = response;
46427         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46428         this.form.afterAction(this, false);
46429     },
46430     
46431     handleResponse : function(response){
46432         if(this.form.errorReader){
46433             var rs = this.form.errorReader.read(response);
46434             var errors = [];
46435             if(rs.records){
46436                 for(var i = 0, len = rs.records.length; i < len; i++) {
46437                     var r = rs.records[i];
46438                     errors[i] = r.data;
46439                 }
46440             }
46441             if(errors.length < 1){
46442                 errors = null;
46443             }
46444             return {
46445                 success : rs.success,
46446                 errors : errors
46447             };
46448         }
46449         var ret = false;
46450         try {
46451             ret = Roo.decode(response.responseText);
46452         } catch (e) {
46453             ret = {
46454                 success: false,
46455                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46456                 errors : []
46457             };
46458         }
46459         return ret;
46460         
46461     }
46462 });
46463
46464
46465 Roo.form.Action.Load = function(form, options){
46466     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46467     this.reader = this.form.reader;
46468 };
46469
46470 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46471     type : 'load',
46472
46473     run : function(){
46474         
46475         Roo.Ajax.request(Roo.apply(
46476                 this.createCallback(), {
46477                     method:this.getMethod(),
46478                     url:this.getUrl(false),
46479                     params:this.getParams()
46480         }));
46481     },
46482
46483     success : function(response){
46484         
46485         var result = this.processResponse(response);
46486         if(result === true || !result.success || !result.data){
46487             this.failureType = Roo.form.Action.LOAD_FAILURE;
46488             this.form.afterAction(this, false);
46489             return;
46490         }
46491         this.form.clearInvalid();
46492         this.form.setValues(result.data);
46493         this.form.afterAction(this, true);
46494     },
46495
46496     handleResponse : function(response){
46497         if(this.form.reader){
46498             var rs = this.form.reader.read(response);
46499             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46500             return {
46501                 success : rs.success,
46502                 data : data
46503             };
46504         }
46505         return Roo.decode(response.responseText);
46506     }
46507 });
46508
46509 Roo.form.Action.ACTION_TYPES = {
46510     'load' : Roo.form.Action.Load,
46511     'submit' : Roo.form.Action.Submit
46512 };/*
46513  * Based on:
46514  * Ext JS Library 1.1.1
46515  * Copyright(c) 2006-2007, Ext JS, LLC.
46516  *
46517  * Originally Released Under LGPL - original licence link has changed is not relivant.
46518  *
46519  * Fork - LGPL
46520  * <script type="text/javascript">
46521  */
46522  
46523 /**
46524  * @class Roo.form.Layout
46525  * @extends Roo.Component
46526  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46527  * @constructor
46528  * @param {Object} config Configuration options
46529  */
46530 Roo.form.Layout = function(config){
46531     var xitems = [];
46532     if (config.items) {
46533         xitems = config.items;
46534         delete config.items;
46535     }
46536     Roo.form.Layout.superclass.constructor.call(this, config);
46537     this.stack = [];
46538     Roo.each(xitems, this.addxtype, this);
46539      
46540 };
46541
46542 Roo.extend(Roo.form.Layout, Roo.Component, {
46543     /**
46544      * @cfg {String/Object} autoCreate
46545      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46546      */
46547     /**
46548      * @cfg {String/Object/Function} style
46549      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46550      * a function which returns such a specification.
46551      */
46552     /**
46553      * @cfg {String} labelAlign
46554      * Valid values are "left," "top" and "right" (defaults to "left")
46555      */
46556     /**
46557      * @cfg {Number} labelWidth
46558      * Fixed width in pixels of all field labels (defaults to undefined)
46559      */
46560     /**
46561      * @cfg {Boolean} clear
46562      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46563      */
46564     clear : true,
46565     /**
46566      * @cfg {String} labelSeparator
46567      * The separator to use after field labels (defaults to ':')
46568      */
46569     labelSeparator : ':',
46570     /**
46571      * @cfg {Boolean} hideLabels
46572      * True to suppress the display of field labels in this layout (defaults to false)
46573      */
46574     hideLabels : false,
46575
46576     // private
46577     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46578     
46579     isLayout : true,
46580     
46581     // private
46582     onRender : function(ct, position){
46583         if(this.el){ // from markup
46584             this.el = Roo.get(this.el);
46585         }else {  // generate
46586             var cfg = this.getAutoCreate();
46587             this.el = ct.createChild(cfg, position);
46588         }
46589         if(this.style){
46590             this.el.applyStyles(this.style);
46591         }
46592         if(this.labelAlign){
46593             this.el.addClass('x-form-label-'+this.labelAlign);
46594         }
46595         if(this.hideLabels){
46596             this.labelStyle = "display:none";
46597             this.elementStyle = "padding-left:0;";
46598         }else{
46599             if(typeof this.labelWidth == 'number'){
46600                 this.labelStyle = "width:"+this.labelWidth+"px;";
46601                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46602             }
46603             if(this.labelAlign == 'top'){
46604                 this.labelStyle = "width:auto;";
46605                 this.elementStyle = "padding-left:0;";
46606             }
46607         }
46608         var stack = this.stack;
46609         var slen = stack.length;
46610         if(slen > 0){
46611             if(!this.fieldTpl){
46612                 var t = new Roo.Template(
46613                     '<div class="x-form-item {5}">',
46614                         '<label for="{0}" style="{2}">{1}{4}</label>',
46615                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46616                         '</div>',
46617                     '</div><div class="x-form-clear-left"></div>'
46618                 );
46619                 t.disableFormats = true;
46620                 t.compile();
46621                 Roo.form.Layout.prototype.fieldTpl = t;
46622             }
46623             for(var i = 0; i < slen; i++) {
46624                 if(stack[i].isFormField){
46625                     this.renderField(stack[i]);
46626                 }else{
46627                     this.renderComponent(stack[i]);
46628                 }
46629             }
46630         }
46631         if(this.clear){
46632             this.el.createChild({cls:'x-form-clear'});
46633         }
46634     },
46635
46636     // private
46637     renderField : function(f){
46638         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46639                f.id, //0
46640                f.fieldLabel, //1
46641                f.labelStyle||this.labelStyle||'', //2
46642                this.elementStyle||'', //3
46643                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46644                f.itemCls||this.itemCls||''  //5
46645        ], true).getPrevSibling());
46646     },
46647
46648     // private
46649     renderComponent : function(c){
46650         c.render(c.isLayout ? this.el : this.el.createChild());    
46651     },
46652     /**
46653      * Adds a object form elements (using the xtype property as the factory method.)
46654      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46655      * @param {Object} config 
46656      */
46657     addxtype : function(o)
46658     {
46659         // create the lement.
46660         o.form = this.form;
46661         var fe = Roo.factory(o, Roo.form);
46662         this.form.allItems.push(fe);
46663         this.stack.push(fe);
46664         
46665         if (fe.isFormField) {
46666             this.form.items.add(fe);
46667         }
46668          
46669         return fe;
46670     }
46671 });
46672
46673 /**
46674  * @class Roo.form.Column
46675  * @extends Roo.form.Layout
46676  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46677  * @constructor
46678  * @param {Object} config Configuration options
46679  */
46680 Roo.form.Column = function(config){
46681     Roo.form.Column.superclass.constructor.call(this, config);
46682 };
46683
46684 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46685     /**
46686      * @cfg {Number/String} width
46687      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46688      */
46689     /**
46690      * @cfg {String/Object} autoCreate
46691      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46692      */
46693
46694     // private
46695     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46696
46697     // private
46698     onRender : function(ct, position){
46699         Roo.form.Column.superclass.onRender.call(this, ct, position);
46700         if(this.width){
46701             this.el.setWidth(this.width);
46702         }
46703     }
46704 });
46705
46706
46707 /**
46708  * @class Roo.form.Row
46709  * @extends Roo.form.Layout
46710  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46711  * @constructor
46712  * @param {Object} config Configuration options
46713  */
46714
46715  
46716 Roo.form.Row = function(config){
46717     Roo.form.Row.superclass.constructor.call(this, config);
46718 };
46719  
46720 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46721       /**
46722      * @cfg {Number/String} width
46723      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46724      */
46725     /**
46726      * @cfg {Number/String} height
46727      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46728      */
46729     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46730     
46731     padWidth : 20,
46732     // private
46733     onRender : function(ct, position){
46734         //console.log('row render');
46735         if(!this.rowTpl){
46736             var t = new Roo.Template(
46737                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46738                     '<label for="{0}" style="{2}">{1}{4}</label>',
46739                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46740                     '</div>',
46741                 '</div>'
46742             );
46743             t.disableFormats = true;
46744             t.compile();
46745             Roo.form.Layout.prototype.rowTpl = t;
46746         }
46747         this.fieldTpl = this.rowTpl;
46748         
46749         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46750         var labelWidth = 100;
46751         
46752         if ((this.labelAlign != 'top')) {
46753             if (typeof this.labelWidth == 'number') {
46754                 labelWidth = this.labelWidth
46755             }
46756             this.padWidth =  20 + labelWidth;
46757             
46758         }
46759         
46760         Roo.form.Column.superclass.onRender.call(this, ct, position);
46761         if(this.width){
46762             this.el.setWidth(this.width);
46763         }
46764         if(this.height){
46765             this.el.setHeight(this.height);
46766         }
46767     },
46768     
46769     // private
46770     renderField : function(f){
46771         f.fieldEl = this.fieldTpl.append(this.el, [
46772                f.id, f.fieldLabel,
46773                f.labelStyle||this.labelStyle||'',
46774                this.elementStyle||'',
46775                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46776                f.itemCls||this.itemCls||'',
46777                f.width ? f.width + this.padWidth : 160 + this.padWidth
46778        ],true);
46779     }
46780 });
46781  
46782
46783 /**
46784  * @class Roo.form.FieldSet
46785  * @extends Roo.form.Layout
46786  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46787  * @constructor
46788  * @param {Object} config Configuration options
46789  */
46790 Roo.form.FieldSet = function(config){
46791     Roo.form.FieldSet.superclass.constructor.call(this, config);
46792 };
46793
46794 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46795     /**
46796      * @cfg {String} legend
46797      * The text to display as the legend for the FieldSet (defaults to '')
46798      */
46799     /**
46800      * @cfg {String/Object} autoCreate
46801      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46802      */
46803
46804     // private
46805     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46806
46807     // private
46808     onRender : function(ct, position){
46809         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46810         if(this.legend){
46811             this.setLegend(this.legend);
46812         }
46813     },
46814
46815     // private
46816     setLegend : function(text){
46817         if(this.rendered){
46818             this.el.child('legend').update(text);
46819         }
46820     }
46821 });/*
46822  * Based on:
46823  * Ext JS Library 1.1.1
46824  * Copyright(c) 2006-2007, Ext JS, LLC.
46825  *
46826  * Originally Released Under LGPL - original licence link has changed is not relivant.
46827  *
46828  * Fork - LGPL
46829  * <script type="text/javascript">
46830  */
46831 /**
46832  * @class Roo.form.VTypes
46833  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46834  * @singleton
46835  */
46836 Roo.form.VTypes = function(){
46837     // closure these in so they are only created once.
46838     var alpha = /^[a-zA-Z_]+$/;
46839     var alphanum = /^[a-zA-Z0-9_]+$/;
46840     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
46841     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46842
46843     // All these messages and functions are configurable
46844     return {
46845         /**
46846          * The function used to validate email addresses
46847          * @param {String} value The email address
46848          */
46849         'email' : function(v){
46850             return email.test(v);
46851         },
46852         /**
46853          * The error text to display when the email validation function returns false
46854          * @type String
46855          */
46856         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46857         /**
46858          * The keystroke filter mask to be applied on email input
46859          * @type RegExp
46860          */
46861         'emailMask' : /[a-z0-9_\.\-@]/i,
46862
46863         /**
46864          * The function used to validate URLs
46865          * @param {String} value The URL
46866          */
46867         'url' : function(v){
46868             return url.test(v);
46869         },
46870         /**
46871          * The error text to display when the url validation function returns false
46872          * @type String
46873          */
46874         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46875         
46876         /**
46877          * The function used to validate alpha values
46878          * @param {String} value The value
46879          */
46880         'alpha' : function(v){
46881             return alpha.test(v);
46882         },
46883         /**
46884          * The error text to display when the alpha validation function returns false
46885          * @type String
46886          */
46887         'alphaText' : 'This field should only contain letters and _',
46888         /**
46889          * The keystroke filter mask to be applied on alpha input
46890          * @type RegExp
46891          */
46892         'alphaMask' : /[a-z_]/i,
46893
46894         /**
46895          * The function used to validate alphanumeric values
46896          * @param {String} value The value
46897          */
46898         'alphanum' : function(v){
46899             return alphanum.test(v);
46900         },
46901         /**
46902          * The error text to display when the alphanumeric validation function returns false
46903          * @type String
46904          */
46905         'alphanumText' : 'This field should only contain letters, numbers and _',
46906         /**
46907          * The keystroke filter mask to be applied on alphanumeric input
46908          * @type RegExp
46909          */
46910         'alphanumMask' : /[a-z0-9_]/i
46911     };
46912 }();//<script type="text/javascript">
46913
46914 /**
46915  * @class Roo.form.FCKeditor
46916  * @extends Roo.form.TextArea
46917  * Wrapper around the FCKEditor http://www.fckeditor.net
46918  * @constructor
46919  * Creates a new FCKeditor
46920  * @param {Object} config Configuration options
46921  */
46922 Roo.form.FCKeditor = function(config){
46923     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46924     this.addEvents({
46925          /**
46926          * @event editorinit
46927          * Fired when the editor is initialized - you can add extra handlers here..
46928          * @param {FCKeditor} this
46929          * @param {Object} the FCK object.
46930          */
46931         editorinit : true
46932     });
46933     
46934     
46935 };
46936 Roo.form.FCKeditor.editors = { };
46937 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46938 {
46939     //defaultAutoCreate : {
46940     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46941     //},
46942     // private
46943     /**
46944      * @cfg {Object} fck options - see fck manual for details.
46945      */
46946     fckconfig : false,
46947     
46948     /**
46949      * @cfg {Object} fck toolbar set (Basic or Default)
46950      */
46951     toolbarSet : 'Basic',
46952     /**
46953      * @cfg {Object} fck BasePath
46954      */ 
46955     basePath : '/fckeditor/',
46956     
46957     
46958     frame : false,
46959     
46960     value : '',
46961     
46962    
46963     onRender : function(ct, position)
46964     {
46965         if(!this.el){
46966             this.defaultAutoCreate = {
46967                 tag: "textarea",
46968                 style:"width:300px;height:60px;",
46969                 autocomplete: "new-password"
46970             };
46971         }
46972         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46973         /*
46974         if(this.grow){
46975             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46976             if(this.preventScrollbars){
46977                 this.el.setStyle("overflow", "hidden");
46978             }
46979             this.el.setHeight(this.growMin);
46980         }
46981         */
46982         //console.log('onrender' + this.getId() );
46983         Roo.form.FCKeditor.editors[this.getId()] = this;
46984          
46985
46986         this.replaceTextarea() ;
46987         
46988     },
46989     
46990     getEditor : function() {
46991         return this.fckEditor;
46992     },
46993     /**
46994      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46995      * @param {Mixed} value The value to set
46996      */
46997     
46998     
46999     setValue : function(value)
47000     {
47001         //console.log('setValue: ' + value);
47002         
47003         if(typeof(value) == 'undefined') { // not sure why this is happending...
47004             return;
47005         }
47006         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
47007         
47008         //if(!this.el || !this.getEditor()) {
47009         //    this.value = value;
47010             //this.setValue.defer(100,this,[value]);    
47011         //    return;
47012         //} 
47013         
47014         if(!this.getEditor()) {
47015             return;
47016         }
47017         
47018         this.getEditor().SetData(value);
47019         
47020         //
47021
47022     },
47023
47024     /**
47025      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
47026      * @return {Mixed} value The field value
47027      */
47028     getValue : function()
47029     {
47030         
47031         if (this.frame && this.frame.dom.style.display == 'none') {
47032             return Roo.form.FCKeditor.superclass.getValue.call(this);
47033         }
47034         
47035         if(!this.el || !this.getEditor()) {
47036            
47037            // this.getValue.defer(100,this); 
47038             return this.value;
47039         }
47040        
47041         
47042         var value=this.getEditor().GetData();
47043         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
47044         return Roo.form.FCKeditor.superclass.getValue.call(this);
47045         
47046
47047     },
47048
47049     /**
47050      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
47051      * @return {Mixed} value The field value
47052      */
47053     getRawValue : function()
47054     {
47055         if (this.frame && this.frame.dom.style.display == 'none') {
47056             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
47057         }
47058         
47059         if(!this.el || !this.getEditor()) {
47060             //this.getRawValue.defer(100,this); 
47061             return this.value;
47062             return;
47063         }
47064         
47065         
47066         
47067         var value=this.getEditor().GetData();
47068         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
47069         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
47070          
47071     },
47072     
47073     setSize : function(w,h) {
47074         
47075         
47076         
47077         //if (this.frame && this.frame.dom.style.display == 'none') {
47078         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47079         //    return;
47080         //}
47081         //if(!this.el || !this.getEditor()) {
47082         //    this.setSize.defer(100,this, [w,h]); 
47083         //    return;
47084         //}
47085         
47086         
47087         
47088         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47089         
47090         this.frame.dom.setAttribute('width', w);
47091         this.frame.dom.setAttribute('height', h);
47092         this.frame.setSize(w,h);
47093         
47094     },
47095     
47096     toggleSourceEdit : function(value) {
47097         
47098       
47099          
47100         this.el.dom.style.display = value ? '' : 'none';
47101         this.frame.dom.style.display = value ?  'none' : '';
47102         
47103     },
47104     
47105     
47106     focus: function(tag)
47107     {
47108         if (this.frame.dom.style.display == 'none') {
47109             return Roo.form.FCKeditor.superclass.focus.call(this);
47110         }
47111         if(!this.el || !this.getEditor()) {
47112             this.focus.defer(100,this, [tag]); 
47113             return;
47114         }
47115         
47116         
47117         
47118         
47119         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47120         this.getEditor().Focus();
47121         if (tgs.length) {
47122             if (!this.getEditor().Selection.GetSelection()) {
47123                 this.focus.defer(100,this, [tag]); 
47124                 return;
47125             }
47126             
47127             
47128             var r = this.getEditor().EditorDocument.createRange();
47129             r.setStart(tgs[0],0);
47130             r.setEnd(tgs[0],0);
47131             this.getEditor().Selection.GetSelection().removeAllRanges();
47132             this.getEditor().Selection.GetSelection().addRange(r);
47133             this.getEditor().Focus();
47134         }
47135         
47136     },
47137     
47138     
47139     
47140     replaceTextarea : function()
47141     {
47142         if ( document.getElementById( this.getId() + '___Frame' ) ) {
47143             return ;
47144         }
47145         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47146         //{
47147             // We must check the elements firstly using the Id and then the name.
47148         var oTextarea = document.getElementById( this.getId() );
47149         
47150         var colElementsByName = document.getElementsByName( this.getId() ) ;
47151          
47152         oTextarea.style.display = 'none' ;
47153
47154         if ( oTextarea.tabIndex ) {            
47155             this.TabIndex = oTextarea.tabIndex ;
47156         }
47157         
47158         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47159         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47160         this.frame = Roo.get(this.getId() + '___Frame')
47161     },
47162     
47163     _getConfigHtml : function()
47164     {
47165         var sConfig = '' ;
47166
47167         for ( var o in this.fckconfig ) {
47168             sConfig += sConfig.length > 0  ? '&amp;' : '';
47169             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47170         }
47171
47172         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47173     },
47174     
47175     
47176     _getIFrameHtml : function()
47177     {
47178         var sFile = 'fckeditor.html' ;
47179         /* no idea what this is about..
47180         try
47181         {
47182             if ( (/fcksource=true/i).test( window.top.location.search ) )
47183                 sFile = 'fckeditor.original.html' ;
47184         }
47185         catch (e) { 
47186         */
47187
47188         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47189         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47190         
47191         
47192         var html = '<iframe id="' + this.getId() +
47193             '___Frame" src="' + sLink +
47194             '" width="' + this.width +
47195             '" height="' + this.height + '"' +
47196             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47197             ' frameborder="0" scrolling="no"></iframe>' ;
47198
47199         return html ;
47200     },
47201     
47202     _insertHtmlBefore : function( html, element )
47203     {
47204         if ( element.insertAdjacentHTML )       {
47205             // IE
47206             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47207         } else { // Gecko
47208             var oRange = document.createRange() ;
47209             oRange.setStartBefore( element ) ;
47210             var oFragment = oRange.createContextualFragment( html );
47211             element.parentNode.insertBefore( oFragment, element ) ;
47212         }
47213     }
47214     
47215     
47216   
47217     
47218     
47219     
47220     
47221
47222 });
47223
47224 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47225
47226 function FCKeditor_OnComplete(editorInstance){
47227     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47228     f.fckEditor = editorInstance;
47229     //console.log("loaded");
47230     f.fireEvent('editorinit', f, editorInstance);
47231
47232   
47233
47234  
47235
47236
47237
47238
47239
47240
47241
47242
47243
47244
47245
47246
47247
47248
47249
47250 //<script type="text/javascript">
47251 /**
47252  * @class Roo.form.GridField
47253  * @extends Roo.form.Field
47254  * Embed a grid (or editable grid into a form)
47255  * STATUS ALPHA
47256  * 
47257  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47258  * it needs 
47259  * xgrid.store = Roo.data.Store
47260  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47261  * xgrid.store.reader = Roo.data.JsonReader 
47262  * 
47263  * 
47264  * @constructor
47265  * Creates a new GridField
47266  * @param {Object} config Configuration options
47267  */
47268 Roo.form.GridField = function(config){
47269     Roo.form.GridField.superclass.constructor.call(this, config);
47270      
47271 };
47272
47273 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47274     /**
47275      * @cfg {Number} width  - used to restrict width of grid..
47276      */
47277     width : 100,
47278     /**
47279      * @cfg {Number} height - used to restrict height of grid..
47280      */
47281     height : 50,
47282      /**
47283      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47284          * 
47285          *}
47286      */
47287     xgrid : false, 
47288     /**
47289      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47290      * {tag: "input", type: "checkbox", autocomplete: "off"})
47291      */
47292    // defaultAutoCreate : { tag: 'div' },
47293     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47294     /**
47295      * @cfg {String} addTitle Text to include for adding a title.
47296      */
47297     addTitle : false,
47298     //
47299     onResize : function(){
47300         Roo.form.Field.superclass.onResize.apply(this, arguments);
47301     },
47302
47303     initEvents : function(){
47304         // Roo.form.Checkbox.superclass.initEvents.call(this);
47305         // has no events...
47306        
47307     },
47308
47309
47310     getResizeEl : function(){
47311         return this.wrap;
47312     },
47313
47314     getPositionEl : function(){
47315         return this.wrap;
47316     },
47317
47318     // private
47319     onRender : function(ct, position){
47320         
47321         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47322         var style = this.style;
47323         delete this.style;
47324         
47325         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47326         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47327         this.viewEl = this.wrap.createChild({ tag: 'div' });
47328         if (style) {
47329             this.viewEl.applyStyles(style);
47330         }
47331         if (this.width) {
47332             this.viewEl.setWidth(this.width);
47333         }
47334         if (this.height) {
47335             this.viewEl.setHeight(this.height);
47336         }
47337         //if(this.inputValue !== undefined){
47338         //this.setValue(this.value);
47339         
47340         
47341         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47342         
47343         
47344         this.grid.render();
47345         this.grid.getDataSource().on('remove', this.refreshValue, this);
47346         this.grid.getDataSource().on('update', this.refreshValue, this);
47347         this.grid.on('afteredit', this.refreshValue, this);
47348  
47349     },
47350      
47351     
47352     /**
47353      * Sets the value of the item. 
47354      * @param {String} either an object  or a string..
47355      */
47356     setValue : function(v){
47357         //this.value = v;
47358         v = v || []; // empty set..
47359         // this does not seem smart - it really only affects memoryproxy grids..
47360         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47361             var ds = this.grid.getDataSource();
47362             // assumes a json reader..
47363             var data = {}
47364             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47365             ds.loadData( data);
47366         }
47367         // clear selection so it does not get stale.
47368         if (this.grid.sm) { 
47369             this.grid.sm.clearSelections();
47370         }
47371         
47372         Roo.form.GridField.superclass.setValue.call(this, v);
47373         this.refreshValue();
47374         // should load data in the grid really....
47375     },
47376     
47377     // private
47378     refreshValue: function() {
47379          var val = [];
47380         this.grid.getDataSource().each(function(r) {
47381             val.push(r.data);
47382         });
47383         this.el.dom.value = Roo.encode(val);
47384     }
47385     
47386      
47387     
47388     
47389 });/*
47390  * Based on:
47391  * Ext JS Library 1.1.1
47392  * Copyright(c) 2006-2007, Ext JS, LLC.
47393  *
47394  * Originally Released Under LGPL - original licence link has changed is not relivant.
47395  *
47396  * Fork - LGPL
47397  * <script type="text/javascript">
47398  */
47399 /**
47400  * @class Roo.form.DisplayField
47401  * @extends Roo.form.Field
47402  * A generic Field to display non-editable data.
47403  * @cfg {Boolean} closable (true|false) default false
47404  * @constructor
47405  * Creates a new Display Field item.
47406  * @param {Object} config Configuration options
47407  */
47408 Roo.form.DisplayField = function(config){
47409     Roo.form.DisplayField.superclass.constructor.call(this, config);
47410     
47411     this.addEvents({
47412         /**
47413          * @event close
47414          * Fires after the click the close btn
47415              * @param {Roo.form.DisplayField} this
47416              */
47417         close : true
47418     });
47419 };
47420
47421 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47422     inputType:      'hidden',
47423     allowBlank:     true,
47424     readOnly:         true,
47425     
47426  
47427     /**
47428      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47429      */
47430     focusClass : undefined,
47431     /**
47432      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47433      */
47434     fieldClass: 'x-form-field',
47435     
47436      /**
47437      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47438      */
47439     valueRenderer: undefined,
47440     
47441     width: 100,
47442     /**
47443      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47444      * {tag: "input", type: "checkbox", autocomplete: "off"})
47445      */
47446      
47447  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47448  
47449     closable : false,
47450     
47451     onResize : function(){
47452         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47453         
47454     },
47455
47456     initEvents : function(){
47457         // Roo.form.Checkbox.superclass.initEvents.call(this);
47458         // has no events...
47459         
47460         if(this.closable){
47461             this.closeEl.on('click', this.onClose, this);
47462         }
47463        
47464     },
47465
47466
47467     getResizeEl : function(){
47468         return this.wrap;
47469     },
47470
47471     getPositionEl : function(){
47472         return this.wrap;
47473     },
47474
47475     // private
47476     onRender : function(ct, position){
47477         
47478         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47479         //if(this.inputValue !== undefined){
47480         this.wrap = this.el.wrap();
47481         
47482         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47483         
47484         if(this.closable){
47485             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
47486         }
47487         
47488         if (this.bodyStyle) {
47489             this.viewEl.applyStyles(this.bodyStyle);
47490         }
47491         //this.viewEl.setStyle('padding', '2px');
47492         
47493         this.setValue(this.value);
47494         
47495     },
47496 /*
47497     // private
47498     initValue : Roo.emptyFn,
47499
47500   */
47501
47502         // private
47503     onClick : function(){
47504         
47505     },
47506
47507     /**
47508      * Sets the checked state of the checkbox.
47509      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47510      */
47511     setValue : function(v){
47512         this.value = v;
47513         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47514         // this might be called before we have a dom element..
47515         if (!this.viewEl) {
47516             return;
47517         }
47518         this.viewEl.dom.innerHTML = html;
47519         Roo.form.DisplayField.superclass.setValue.call(this, v);
47520
47521     },
47522     
47523     onClose : function(e)
47524     {
47525         e.preventDefault();
47526         
47527         this.fireEvent('close', this);
47528     }
47529 });/*
47530  * 
47531  * Licence- LGPL
47532  * 
47533  */
47534
47535 /**
47536  * @class Roo.form.DayPicker
47537  * @extends Roo.form.Field
47538  * A Day picker show [M] [T] [W] ....
47539  * @constructor
47540  * Creates a new Day Picker
47541  * @param {Object} config Configuration options
47542  */
47543 Roo.form.DayPicker= function(config){
47544     Roo.form.DayPicker.superclass.constructor.call(this, config);
47545      
47546 };
47547
47548 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47549     /**
47550      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47551      */
47552     focusClass : undefined,
47553     /**
47554      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47555      */
47556     fieldClass: "x-form-field",
47557    
47558     /**
47559      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47560      * {tag: "input", type: "checkbox", autocomplete: "off"})
47561      */
47562     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47563     
47564    
47565     actionMode : 'viewEl', 
47566     //
47567     // private
47568  
47569     inputType : 'hidden',
47570     
47571      
47572     inputElement: false, // real input element?
47573     basedOn: false, // ????
47574     
47575     isFormField: true, // not sure where this is needed!!!!
47576
47577     onResize : function(){
47578         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47579         if(!this.boxLabel){
47580             this.el.alignTo(this.wrap, 'c-c');
47581         }
47582     },
47583
47584     initEvents : function(){
47585         Roo.form.Checkbox.superclass.initEvents.call(this);
47586         this.el.on("click", this.onClick,  this);
47587         this.el.on("change", this.onClick,  this);
47588     },
47589
47590
47591     getResizeEl : function(){
47592         return this.wrap;
47593     },
47594
47595     getPositionEl : function(){
47596         return this.wrap;
47597     },
47598
47599     
47600     // private
47601     onRender : function(ct, position){
47602         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47603        
47604         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47605         
47606         var r1 = '<table><tr>';
47607         var r2 = '<tr class="x-form-daypick-icons">';
47608         for (var i=0; i < 7; i++) {
47609             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47610             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47611         }
47612         
47613         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47614         viewEl.select('img').on('click', this.onClick, this);
47615         this.viewEl = viewEl;   
47616         
47617         
47618         // this will not work on Chrome!!!
47619         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47620         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47621         
47622         
47623           
47624
47625     },
47626
47627     // private
47628     initValue : Roo.emptyFn,
47629
47630     /**
47631      * Returns the checked state of the checkbox.
47632      * @return {Boolean} True if checked, else false
47633      */
47634     getValue : function(){
47635         return this.el.dom.value;
47636         
47637     },
47638
47639         // private
47640     onClick : function(e){ 
47641         //this.setChecked(!this.checked);
47642         Roo.get(e.target).toggleClass('x-menu-item-checked');
47643         this.refreshValue();
47644         //if(this.el.dom.checked != this.checked){
47645         //    this.setValue(this.el.dom.checked);
47646        // }
47647     },
47648     
47649     // private
47650     refreshValue : function()
47651     {
47652         var val = '';
47653         this.viewEl.select('img',true).each(function(e,i,n)  {
47654             val += e.is(".x-menu-item-checked") ? String(n) : '';
47655         });
47656         this.setValue(val, true);
47657     },
47658
47659     /**
47660      * Sets the checked state of the checkbox.
47661      * On is always based on a string comparison between inputValue and the param.
47662      * @param {Boolean/String} value - the value to set 
47663      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47664      */
47665     setValue : function(v,suppressEvent){
47666         if (!this.el.dom) {
47667             return;
47668         }
47669         var old = this.el.dom.value ;
47670         this.el.dom.value = v;
47671         if (suppressEvent) {
47672             return ;
47673         }
47674          
47675         // update display..
47676         this.viewEl.select('img',true).each(function(e,i,n)  {
47677             
47678             var on = e.is(".x-menu-item-checked");
47679             var newv = v.indexOf(String(n)) > -1;
47680             if (on != newv) {
47681                 e.toggleClass('x-menu-item-checked');
47682             }
47683             
47684         });
47685         
47686         
47687         this.fireEvent('change', this, v, old);
47688         
47689         
47690     },
47691    
47692     // handle setting of hidden value by some other method!!?!?
47693     setFromHidden: function()
47694     {
47695         if(!this.el){
47696             return;
47697         }
47698         //console.log("SET FROM HIDDEN");
47699         //alert('setFrom hidden');
47700         this.setValue(this.el.dom.value);
47701     },
47702     
47703     onDestroy : function()
47704     {
47705         if(this.viewEl){
47706             Roo.get(this.viewEl).remove();
47707         }
47708          
47709         Roo.form.DayPicker.superclass.onDestroy.call(this);
47710     }
47711
47712 });/*
47713  * RooJS Library 1.1.1
47714  * Copyright(c) 2008-2011  Alan Knowles
47715  *
47716  * License - LGPL
47717  */
47718  
47719
47720 /**
47721  * @class Roo.form.ComboCheck
47722  * @extends Roo.form.ComboBox
47723  * A combobox for multiple select items.
47724  *
47725  * FIXME - could do with a reset button..
47726  * 
47727  * @constructor
47728  * Create a new ComboCheck
47729  * @param {Object} config Configuration options
47730  */
47731 Roo.form.ComboCheck = function(config){
47732     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47733     // should verify some data...
47734     // like
47735     // hiddenName = required..
47736     // displayField = required
47737     // valudField == required
47738     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47739     var _t = this;
47740     Roo.each(req, function(e) {
47741         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47742             throw "Roo.form.ComboCheck : missing value for: " + e;
47743         }
47744     });
47745     
47746     
47747 };
47748
47749 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47750      
47751      
47752     editable : false,
47753      
47754     selectedClass: 'x-menu-item-checked', 
47755     
47756     // private
47757     onRender : function(ct, position){
47758         var _t = this;
47759         
47760         
47761         
47762         if(!this.tpl){
47763             var cls = 'x-combo-list';
47764
47765             
47766             this.tpl =  new Roo.Template({
47767                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47768                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47769                    '<span>{' + this.displayField + '}</span>' +
47770                     '</div>' 
47771                 
47772             });
47773         }
47774  
47775         
47776         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47777         this.view.singleSelect = false;
47778         this.view.multiSelect = true;
47779         this.view.toggleSelect = true;
47780         this.pageTb.add(new Roo.Toolbar.Fill(), {
47781             
47782             text: 'Done',
47783             handler: function()
47784             {
47785                 _t.collapse();
47786             }
47787         });
47788     },
47789     
47790     onViewOver : function(e, t){
47791         // do nothing...
47792         return;
47793         
47794     },
47795     
47796     onViewClick : function(doFocus,index){
47797         return;
47798         
47799     },
47800     select: function () {
47801         //Roo.log("SELECT CALLED");
47802     },
47803      
47804     selectByValue : function(xv, scrollIntoView){
47805         var ar = this.getValueArray();
47806         var sels = [];
47807         
47808         Roo.each(ar, function(v) {
47809             if(v === undefined || v === null){
47810                 return;
47811             }
47812             var r = this.findRecord(this.valueField, v);
47813             if(r){
47814                 sels.push(this.store.indexOf(r))
47815                 
47816             }
47817         },this);
47818         this.view.select(sels);
47819         return false;
47820     },
47821     
47822     
47823     
47824     onSelect : function(record, index){
47825        // Roo.log("onselect Called");
47826        // this is only called by the clear button now..
47827         this.view.clearSelections();
47828         this.setValue('[]');
47829         if (this.value != this.valueBefore) {
47830             this.fireEvent('change', this, this.value, this.valueBefore);
47831             this.valueBefore = this.value;
47832         }
47833     },
47834     getValueArray : function()
47835     {
47836         var ar = [] ;
47837         
47838         try {
47839             //Roo.log(this.value);
47840             if (typeof(this.value) == 'undefined') {
47841                 return [];
47842             }
47843             var ar = Roo.decode(this.value);
47844             return  ar instanceof Array ? ar : []; //?? valid?
47845             
47846         } catch(e) {
47847             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47848             return [];
47849         }
47850          
47851     },
47852     expand : function ()
47853     {
47854         
47855         Roo.form.ComboCheck.superclass.expand.call(this);
47856         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47857         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47858         
47859
47860     },
47861     
47862     collapse : function(){
47863         Roo.form.ComboCheck.superclass.collapse.call(this);
47864         var sl = this.view.getSelectedIndexes();
47865         var st = this.store;
47866         var nv = [];
47867         var tv = [];
47868         var r;
47869         Roo.each(sl, function(i) {
47870             r = st.getAt(i);
47871             nv.push(r.get(this.valueField));
47872         },this);
47873         this.setValue(Roo.encode(nv));
47874         if (this.value != this.valueBefore) {
47875
47876             this.fireEvent('change', this, this.value, this.valueBefore);
47877             this.valueBefore = this.value;
47878         }
47879         
47880     },
47881     
47882     setValue : function(v){
47883         // Roo.log(v);
47884         this.value = v;
47885         
47886         var vals = this.getValueArray();
47887         var tv = [];
47888         Roo.each(vals, function(k) {
47889             var r = this.findRecord(this.valueField, k);
47890             if(r){
47891                 tv.push(r.data[this.displayField]);
47892             }else if(this.valueNotFoundText !== undefined){
47893                 tv.push( this.valueNotFoundText );
47894             }
47895         },this);
47896        // Roo.log(tv);
47897         
47898         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47899         this.hiddenField.value = v;
47900         this.value = v;
47901     }
47902     
47903 });/*
47904  * Based on:
47905  * Ext JS Library 1.1.1
47906  * Copyright(c) 2006-2007, Ext JS, LLC.
47907  *
47908  * Originally Released Under LGPL - original licence link has changed is not relivant.
47909  *
47910  * Fork - LGPL
47911  * <script type="text/javascript">
47912  */
47913  
47914 /**
47915  * @class Roo.form.Signature
47916  * @extends Roo.form.Field
47917  * Signature field.  
47918  * @constructor
47919  * 
47920  * @param {Object} config Configuration options
47921  */
47922
47923 Roo.form.Signature = function(config){
47924     Roo.form.Signature.superclass.constructor.call(this, config);
47925     
47926     this.addEvents({// not in used??
47927          /**
47928          * @event confirm
47929          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47930              * @param {Roo.form.Signature} combo This combo box
47931              */
47932         'confirm' : true,
47933         /**
47934          * @event reset
47935          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47936              * @param {Roo.form.ComboBox} combo This combo box
47937              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47938              */
47939         'reset' : true
47940     });
47941 };
47942
47943 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47944     /**
47945      * @cfg {Object} labels Label to use when rendering a form.
47946      * defaults to 
47947      * labels : { 
47948      *      clear : "Clear",
47949      *      confirm : "Confirm"
47950      *  }
47951      */
47952     labels : { 
47953         clear : "Clear",
47954         confirm : "Confirm"
47955     },
47956     /**
47957      * @cfg {Number} width The signature panel width (defaults to 300)
47958      */
47959     width: 300,
47960     /**
47961      * @cfg {Number} height The signature panel height (defaults to 100)
47962      */
47963     height : 100,
47964     /**
47965      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47966      */
47967     allowBlank : false,
47968     
47969     //private
47970     // {Object} signPanel The signature SVG panel element (defaults to {})
47971     signPanel : {},
47972     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47973     isMouseDown : false,
47974     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47975     isConfirmed : false,
47976     // {String} signatureTmp SVG mapping string (defaults to empty string)
47977     signatureTmp : '',
47978     
47979     
47980     defaultAutoCreate : { // modified by initCompnoent..
47981         tag: "input",
47982         type:"hidden"
47983     },
47984
47985     // private
47986     onRender : function(ct, position){
47987         
47988         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47989         
47990         this.wrap = this.el.wrap({
47991             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47992         });
47993         
47994         this.createToolbar(this);
47995         this.signPanel = this.wrap.createChild({
47996                 tag: 'div',
47997                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47998             }, this.el
47999         );
48000             
48001         this.svgID = Roo.id();
48002         this.svgEl = this.signPanel.createChild({
48003               xmlns : 'http://www.w3.org/2000/svg',
48004               tag : 'svg',
48005               id : this.svgID + "-svg",
48006               width: this.width,
48007               height: this.height,
48008               viewBox: '0 0 '+this.width+' '+this.height,
48009               cn : [
48010                 {
48011                     tag: "rect",
48012                     id: this.svgID + "-svg-r",
48013                     width: this.width,
48014                     height: this.height,
48015                     fill: "#ffa"
48016                 },
48017                 {
48018                     tag: "line",
48019                     id: this.svgID + "-svg-l",
48020                     x1: "0", // start
48021                     y1: (this.height*0.8), // start set the line in 80% of height
48022                     x2: this.width, // end
48023                     y2: (this.height*0.8), // end set the line in 80% of height
48024                     'stroke': "#666",
48025                     'stroke-width': "1",
48026                     'stroke-dasharray': "3",
48027                     'shape-rendering': "crispEdges",
48028                     'pointer-events': "none"
48029                 },
48030                 {
48031                     tag: "path",
48032                     id: this.svgID + "-svg-p",
48033                     'stroke': "navy",
48034                     'stroke-width': "3",
48035                     'fill': "none",
48036                     'pointer-events': 'none'
48037                 }
48038               ]
48039         });
48040         this.createSVG();
48041         this.svgBox = this.svgEl.dom.getScreenCTM();
48042     },
48043     createSVG : function(){ 
48044         var svg = this.signPanel;
48045         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
48046         var t = this;
48047
48048         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
48049         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
48050         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
48051         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
48052         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
48053         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
48054         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
48055         
48056     },
48057     isTouchEvent : function(e){
48058         return e.type.match(/^touch/);
48059     },
48060     getCoords : function (e) {
48061         var pt    = this.svgEl.dom.createSVGPoint();
48062         pt.x = e.clientX; 
48063         pt.y = e.clientY;
48064         if (this.isTouchEvent(e)) {
48065             pt.x =  e.targetTouches[0].clientX;
48066             pt.y = e.targetTouches[0].clientY;
48067         }
48068         var a = this.svgEl.dom.getScreenCTM();
48069         var b = a.inverse();
48070         var mx = pt.matrixTransform(b);
48071         return mx.x + ',' + mx.y;
48072     },
48073     //mouse event headler 
48074     down : function (e) {
48075         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
48076         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
48077         
48078         this.isMouseDown = true;
48079         
48080         e.preventDefault();
48081     },
48082     move : function (e) {
48083         if (this.isMouseDown) {
48084             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
48085             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
48086         }
48087         
48088         e.preventDefault();
48089     },
48090     up : function (e) {
48091         this.isMouseDown = false;
48092         var sp = this.signatureTmp.split(' ');
48093         
48094         if(sp.length > 1){
48095             if(!sp[sp.length-2].match(/^L/)){
48096                 sp.pop();
48097                 sp.pop();
48098                 sp.push("");
48099                 this.signatureTmp = sp.join(" ");
48100             }
48101         }
48102         if(this.getValue() != this.signatureTmp){
48103             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48104             this.isConfirmed = false;
48105         }
48106         e.preventDefault();
48107     },
48108     
48109     /**
48110      * Protected method that will not generally be called directly. It
48111      * is called when the editor creates its toolbar. Override this method if you need to
48112      * add custom toolbar buttons.
48113      * @param {HtmlEditor} editor
48114      */
48115     createToolbar : function(editor){
48116          function btn(id, toggle, handler){
48117             var xid = fid + '-'+ id ;
48118             return {
48119                 id : xid,
48120                 cmd : id,
48121                 cls : 'x-btn-icon x-edit-'+id,
48122                 enableToggle:toggle !== false,
48123                 scope: editor, // was editor...
48124                 handler:handler||editor.relayBtnCmd,
48125                 clickEvent:'mousedown',
48126                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48127                 tabIndex:-1
48128             };
48129         }
48130         
48131         
48132         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48133         this.tb = tb;
48134         this.tb.add(
48135            {
48136                 cls : ' x-signature-btn x-signature-'+id,
48137                 scope: editor, // was editor...
48138                 handler: this.reset,
48139                 clickEvent:'mousedown',
48140                 text: this.labels.clear
48141             },
48142             {
48143                  xtype : 'Fill',
48144                  xns: Roo.Toolbar
48145             }, 
48146             {
48147                 cls : '  x-signature-btn x-signature-'+id,
48148                 scope: editor, // was editor...
48149                 handler: this.confirmHandler,
48150                 clickEvent:'mousedown',
48151                 text: this.labels.confirm
48152             }
48153         );
48154     
48155     },
48156     //public
48157     /**
48158      * when user is clicked confirm then show this image.....
48159      * 
48160      * @return {String} Image Data URI
48161      */
48162     getImageDataURI : function(){
48163         var svg = this.svgEl.dom.parentNode.innerHTML;
48164         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48165         return src; 
48166     },
48167     /**
48168      * 
48169      * @return {Boolean} this.isConfirmed
48170      */
48171     getConfirmed : function(){
48172         return this.isConfirmed;
48173     },
48174     /**
48175      * 
48176      * @return {Number} this.width
48177      */
48178     getWidth : function(){
48179         return this.width;
48180     },
48181     /**
48182      * 
48183      * @return {Number} this.height
48184      */
48185     getHeight : function(){
48186         return this.height;
48187     },
48188     // private
48189     getSignature : function(){
48190         return this.signatureTmp;
48191     },
48192     // private
48193     reset : function(){
48194         this.signatureTmp = '';
48195         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48196         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48197         this.isConfirmed = false;
48198         Roo.form.Signature.superclass.reset.call(this);
48199     },
48200     setSignature : function(s){
48201         this.signatureTmp = s;
48202         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48203         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48204         this.setValue(s);
48205         this.isConfirmed = false;
48206         Roo.form.Signature.superclass.reset.call(this);
48207     }, 
48208     test : function(){
48209 //        Roo.log(this.signPanel.dom.contentWindow.up())
48210     },
48211     //private
48212     setConfirmed : function(){
48213         
48214         
48215         
48216 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48217     },
48218     // private
48219     confirmHandler : function(){
48220         if(!this.getSignature()){
48221             return;
48222         }
48223         
48224         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48225         this.setValue(this.getSignature());
48226         this.isConfirmed = true;
48227         
48228         this.fireEvent('confirm', this);
48229     },
48230     // private
48231     // Subclasses should provide the validation implementation by overriding this
48232     validateValue : function(value){
48233         if(this.allowBlank){
48234             return true;
48235         }
48236         
48237         if(this.isConfirmed){
48238             return true;
48239         }
48240         return false;
48241     }
48242 });/*
48243  * Based on:
48244  * Ext JS Library 1.1.1
48245  * Copyright(c) 2006-2007, Ext JS, LLC.
48246  *
48247  * Originally Released Under LGPL - original licence link has changed is not relivant.
48248  *
48249  * Fork - LGPL
48250  * <script type="text/javascript">
48251  */
48252  
48253
48254 /**
48255  * @class Roo.form.ComboBox
48256  * @extends Roo.form.TriggerField
48257  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48258  * @constructor
48259  * Create a new ComboBox.
48260  * @param {Object} config Configuration options
48261  */
48262 Roo.form.Select = function(config){
48263     Roo.form.Select.superclass.constructor.call(this, config);
48264      
48265 };
48266
48267 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48268     /**
48269      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48270      */
48271     /**
48272      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48273      * rendering into an Roo.Editor, defaults to false)
48274      */
48275     /**
48276      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48277      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48278      */
48279     /**
48280      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48281      */
48282     /**
48283      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48284      * the dropdown list (defaults to undefined, with no header element)
48285      */
48286
48287      /**
48288      * @cfg {String/Roo.Template} tpl The template to use to render the output
48289      */
48290      
48291     // private
48292     defaultAutoCreate : {tag: "select"  },
48293     /**
48294      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48295      */
48296     listWidth: undefined,
48297     /**
48298      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48299      * mode = 'remote' or 'text' if mode = 'local')
48300      */
48301     displayField: undefined,
48302     /**
48303      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48304      * mode = 'remote' or 'value' if mode = 'local'). 
48305      * Note: use of a valueField requires the user make a selection
48306      * in order for a value to be mapped.
48307      */
48308     valueField: undefined,
48309     
48310     
48311     /**
48312      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48313      * field's data value (defaults to the underlying DOM element's name)
48314      */
48315     hiddenName: undefined,
48316     /**
48317      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48318      */
48319     listClass: '',
48320     /**
48321      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48322      */
48323     selectedClass: 'x-combo-selected',
48324     /**
48325      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48326      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48327      * which displays a downward arrow icon).
48328      */
48329     triggerClass : 'x-form-arrow-trigger',
48330     /**
48331      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48332      */
48333     shadow:'sides',
48334     /**
48335      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48336      * anchor positions (defaults to 'tl-bl')
48337      */
48338     listAlign: 'tl-bl?',
48339     /**
48340      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48341      */
48342     maxHeight: 300,
48343     /**
48344      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48345      * query specified by the allQuery config option (defaults to 'query')
48346      */
48347     triggerAction: 'query',
48348     /**
48349      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48350      * (defaults to 4, does not apply if editable = false)
48351      */
48352     minChars : 4,
48353     /**
48354      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48355      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48356      */
48357     typeAhead: false,
48358     /**
48359      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48360      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48361      */
48362     queryDelay: 500,
48363     /**
48364      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48365      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48366      */
48367     pageSize: 0,
48368     /**
48369      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48370      * when editable = true (defaults to false)
48371      */
48372     selectOnFocus:false,
48373     /**
48374      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48375      */
48376     queryParam: 'query',
48377     /**
48378      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48379      * when mode = 'remote' (defaults to 'Loading...')
48380      */
48381     loadingText: 'Loading...',
48382     /**
48383      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48384      */
48385     resizable: false,
48386     /**
48387      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48388      */
48389     handleHeight : 8,
48390     /**
48391      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48392      * traditional select (defaults to true)
48393      */
48394     editable: true,
48395     /**
48396      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48397      */
48398     allQuery: '',
48399     /**
48400      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48401      */
48402     mode: 'remote',
48403     /**
48404      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48405      * listWidth has a higher value)
48406      */
48407     minListWidth : 70,
48408     /**
48409      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48410      * allow the user to set arbitrary text into the field (defaults to false)
48411      */
48412     forceSelection:false,
48413     /**
48414      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48415      * if typeAhead = true (defaults to 250)
48416      */
48417     typeAheadDelay : 250,
48418     /**
48419      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48420      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48421      */
48422     valueNotFoundText : undefined,
48423     
48424     /**
48425      * @cfg {String} defaultValue The value displayed after loading the store.
48426      */
48427     defaultValue: '',
48428     
48429     /**
48430      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48431      */
48432     blockFocus : false,
48433     
48434     /**
48435      * @cfg {Boolean} disableClear Disable showing of clear button.
48436      */
48437     disableClear : false,
48438     /**
48439      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48440      */
48441     alwaysQuery : false,
48442     
48443     //private
48444     addicon : false,
48445     editicon: false,
48446     
48447     // element that contains real text value.. (when hidden is used..)
48448      
48449     // private
48450     onRender : function(ct, position){
48451         Roo.form.Field.prototype.onRender.call(this, ct, position);
48452         
48453         if(this.store){
48454             this.store.on('beforeload', this.onBeforeLoad, this);
48455             this.store.on('load', this.onLoad, this);
48456             this.store.on('loadexception', this.onLoadException, this);
48457             this.store.load({});
48458         }
48459         
48460         
48461         
48462     },
48463
48464     // private
48465     initEvents : function(){
48466         //Roo.form.ComboBox.superclass.initEvents.call(this);
48467  
48468     },
48469
48470     onDestroy : function(){
48471        
48472         if(this.store){
48473             this.store.un('beforeload', this.onBeforeLoad, this);
48474             this.store.un('load', this.onLoad, this);
48475             this.store.un('loadexception', this.onLoadException, this);
48476         }
48477         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48478     },
48479
48480     // private
48481     fireKey : function(e){
48482         if(e.isNavKeyPress() && !this.list.isVisible()){
48483             this.fireEvent("specialkey", this, e);
48484         }
48485     },
48486
48487     // private
48488     onResize: function(w, h){
48489         
48490         return; 
48491     
48492         
48493     },
48494
48495     /**
48496      * Allow or prevent the user from directly editing the field text.  If false is passed,
48497      * the user will only be able to select from the items defined in the dropdown list.  This method
48498      * is the runtime equivalent of setting the 'editable' config option at config time.
48499      * @param {Boolean} value True to allow the user to directly edit the field text
48500      */
48501     setEditable : function(value){
48502          
48503     },
48504
48505     // private
48506     onBeforeLoad : function(){
48507         
48508         Roo.log("Select before load");
48509         return;
48510     
48511         this.innerList.update(this.loadingText ?
48512                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48513         //this.restrictHeight();
48514         this.selectedIndex = -1;
48515     },
48516
48517     // private
48518     onLoad : function(){
48519
48520     
48521         var dom = this.el.dom;
48522         dom.innerHTML = '';
48523          var od = dom.ownerDocument;
48524          
48525         if (this.emptyText) {
48526             var op = od.createElement('option');
48527             op.setAttribute('value', '');
48528             op.innerHTML = String.format('{0}', this.emptyText);
48529             dom.appendChild(op);
48530         }
48531         if(this.store.getCount() > 0){
48532            
48533             var vf = this.valueField;
48534             var df = this.displayField;
48535             this.store.data.each(function(r) {
48536                 // which colmsn to use... testing - cdoe / title..
48537                 var op = od.createElement('option');
48538                 op.setAttribute('value', r.data[vf]);
48539                 op.innerHTML = String.format('{0}', r.data[df]);
48540                 dom.appendChild(op);
48541             });
48542             if (typeof(this.defaultValue != 'undefined')) {
48543                 this.setValue(this.defaultValue);
48544             }
48545             
48546              
48547         }else{
48548             //this.onEmptyResults();
48549         }
48550         //this.el.focus();
48551     },
48552     // private
48553     onLoadException : function()
48554     {
48555         dom.innerHTML = '';
48556             
48557         Roo.log("Select on load exception");
48558         return;
48559     
48560         this.collapse();
48561         Roo.log(this.store.reader.jsonData);
48562         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48563             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48564         }
48565         
48566         
48567     },
48568     // private
48569     onTypeAhead : function(){
48570          
48571     },
48572
48573     // private
48574     onSelect : function(record, index){
48575         Roo.log('on select?');
48576         return;
48577         if(this.fireEvent('beforeselect', this, record, index) !== false){
48578             this.setFromData(index > -1 ? record.data : false);
48579             this.collapse();
48580             this.fireEvent('select', this, record, index);
48581         }
48582     },
48583
48584     /**
48585      * Returns the currently selected field value or empty string if no value is set.
48586      * @return {String} value The selected value
48587      */
48588     getValue : function(){
48589         var dom = this.el.dom;
48590         this.value = dom.options[dom.selectedIndex].value;
48591         return this.value;
48592         
48593     },
48594
48595     /**
48596      * Clears any text/value currently set in the field
48597      */
48598     clearValue : function(){
48599         this.value = '';
48600         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48601         
48602     },
48603
48604     /**
48605      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48606      * will be displayed in the field.  If the value does not match the data value of an existing item,
48607      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48608      * Otherwise the field will be blank (although the value will still be set).
48609      * @param {String} value The value to match
48610      */
48611     setValue : function(v){
48612         var d = this.el.dom;
48613         for (var i =0; i < d.options.length;i++) {
48614             if (v == d.options[i].value) {
48615                 d.selectedIndex = i;
48616                 this.value = v;
48617                 return;
48618             }
48619         }
48620         this.clearValue();
48621     },
48622     /**
48623      * @property {Object} the last set data for the element
48624      */
48625     
48626     lastData : false,
48627     /**
48628      * Sets the value of the field based on a object which is related to the record format for the store.
48629      * @param {Object} value the value to set as. or false on reset?
48630      */
48631     setFromData : function(o){
48632         Roo.log('setfrom data?');
48633          
48634         
48635         
48636     },
48637     // private
48638     reset : function(){
48639         this.clearValue();
48640     },
48641     // private
48642     findRecord : function(prop, value){
48643         
48644         return false;
48645     
48646         var record;
48647         if(this.store.getCount() > 0){
48648             this.store.each(function(r){
48649                 if(r.data[prop] == value){
48650                     record = r;
48651                     return false;
48652                 }
48653                 return true;
48654             });
48655         }
48656         return record;
48657     },
48658     
48659     getName: function()
48660     {
48661         // returns hidden if it's set..
48662         if (!this.rendered) {return ''};
48663         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48664         
48665     },
48666      
48667
48668     
48669
48670     // private
48671     onEmptyResults : function(){
48672         Roo.log('empty results');
48673         //this.collapse();
48674     },
48675
48676     /**
48677      * Returns true if the dropdown list is expanded, else false.
48678      */
48679     isExpanded : function(){
48680         return false;
48681     },
48682
48683     /**
48684      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48685      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48686      * @param {String} value The data value of the item to select
48687      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48688      * selected item if it is not currently in view (defaults to true)
48689      * @return {Boolean} True if the value matched an item in the list, else false
48690      */
48691     selectByValue : function(v, scrollIntoView){
48692         Roo.log('select By Value');
48693         return false;
48694     
48695         if(v !== undefined && v !== null){
48696             var r = this.findRecord(this.valueField || this.displayField, v);
48697             if(r){
48698                 this.select(this.store.indexOf(r), scrollIntoView);
48699                 return true;
48700             }
48701         }
48702         return false;
48703     },
48704
48705     /**
48706      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48707      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48708      * @param {Number} index The zero-based index of the list item to select
48709      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48710      * selected item if it is not currently in view (defaults to true)
48711      */
48712     select : function(index, scrollIntoView){
48713         Roo.log('select ');
48714         return  ;
48715         
48716         this.selectedIndex = index;
48717         this.view.select(index);
48718         if(scrollIntoView !== false){
48719             var el = this.view.getNode(index);
48720             if(el){
48721                 this.innerList.scrollChildIntoView(el, false);
48722             }
48723         }
48724     },
48725
48726       
48727
48728     // private
48729     validateBlur : function(){
48730         
48731         return;
48732         
48733     },
48734
48735     // private
48736     initQuery : function(){
48737         this.doQuery(this.getRawValue());
48738     },
48739
48740     // private
48741     doForce : function(){
48742         if(this.el.dom.value.length > 0){
48743             this.el.dom.value =
48744                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48745              
48746         }
48747     },
48748
48749     /**
48750      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48751      * query allowing the query action to be canceled if needed.
48752      * @param {String} query The SQL query to execute
48753      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48754      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48755      * saved in the current store (defaults to false)
48756      */
48757     doQuery : function(q, forceAll){
48758         
48759         Roo.log('doQuery?');
48760         if(q === undefined || q === null){
48761             q = '';
48762         }
48763         var qe = {
48764             query: q,
48765             forceAll: forceAll,
48766             combo: this,
48767             cancel:false
48768         };
48769         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48770             return false;
48771         }
48772         q = qe.query;
48773         forceAll = qe.forceAll;
48774         if(forceAll === true || (q.length >= this.minChars)){
48775             if(this.lastQuery != q || this.alwaysQuery){
48776                 this.lastQuery = q;
48777                 if(this.mode == 'local'){
48778                     this.selectedIndex = -1;
48779                     if(forceAll){
48780                         this.store.clearFilter();
48781                     }else{
48782                         this.store.filter(this.displayField, q);
48783                     }
48784                     this.onLoad();
48785                 }else{
48786                     this.store.baseParams[this.queryParam] = q;
48787                     this.store.load({
48788                         params: this.getParams(q)
48789                     });
48790                     this.expand();
48791                 }
48792             }else{
48793                 this.selectedIndex = -1;
48794                 this.onLoad();   
48795             }
48796         }
48797     },
48798
48799     // private
48800     getParams : function(q){
48801         var p = {};
48802         //p[this.queryParam] = q;
48803         if(this.pageSize){
48804             p.start = 0;
48805             p.limit = this.pageSize;
48806         }
48807         return p;
48808     },
48809
48810     /**
48811      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48812      */
48813     collapse : function(){
48814         
48815     },
48816
48817     // private
48818     collapseIf : function(e){
48819         
48820     },
48821
48822     /**
48823      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48824      */
48825     expand : function(){
48826         
48827     } ,
48828
48829     // private
48830      
48831
48832     /** 
48833     * @cfg {Boolean} grow 
48834     * @hide 
48835     */
48836     /** 
48837     * @cfg {Number} growMin 
48838     * @hide 
48839     */
48840     /** 
48841     * @cfg {Number} growMax 
48842     * @hide 
48843     */
48844     /**
48845      * @hide
48846      * @method autoSize
48847      */
48848     
48849     setWidth : function()
48850     {
48851         
48852     },
48853     getResizeEl : function(){
48854         return this.el;
48855     }
48856 });//<script type="text/javasscript">
48857  
48858
48859 /**
48860  * @class Roo.DDView
48861  * A DnD enabled version of Roo.View.
48862  * @param {Element/String} container The Element in which to create the View.
48863  * @param {String} tpl The template string used to create the markup for each element of the View
48864  * @param {Object} config The configuration properties. These include all the config options of
48865  * {@link Roo.View} plus some specific to this class.<br>
48866  * <p>
48867  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48868  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48869  * <p>
48870  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48871 .x-view-drag-insert-above {
48872         border-top:1px dotted #3366cc;
48873 }
48874 .x-view-drag-insert-below {
48875         border-bottom:1px dotted #3366cc;
48876 }
48877 </code></pre>
48878  * 
48879  */
48880  
48881 Roo.DDView = function(container, tpl, config) {
48882     Roo.DDView.superclass.constructor.apply(this, arguments);
48883     this.getEl().setStyle("outline", "0px none");
48884     this.getEl().unselectable();
48885     if (this.dragGroup) {
48886                 this.setDraggable(this.dragGroup.split(","));
48887     }
48888     if (this.dropGroup) {
48889                 this.setDroppable(this.dropGroup.split(","));
48890     }
48891     if (this.deletable) {
48892         this.setDeletable();
48893     }
48894     this.isDirtyFlag = false;
48895         this.addEvents({
48896                 "drop" : true
48897         });
48898 };
48899
48900 Roo.extend(Roo.DDView, Roo.View, {
48901 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48902 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48903 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48904 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48905
48906         isFormField: true,
48907
48908         reset: Roo.emptyFn,
48909         
48910         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48911
48912         validate: function() {
48913                 return true;
48914         },
48915         
48916         destroy: function() {
48917                 this.purgeListeners();
48918                 this.getEl.removeAllListeners();
48919                 this.getEl().remove();
48920                 if (this.dragZone) {
48921                         if (this.dragZone.destroy) {
48922                                 this.dragZone.destroy();
48923                         }
48924                 }
48925                 if (this.dropZone) {
48926                         if (this.dropZone.destroy) {
48927                                 this.dropZone.destroy();
48928                         }
48929                 }
48930         },
48931
48932 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48933         getName: function() {
48934                 return this.name;
48935         },
48936
48937 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48938         setValue: function(v) {
48939                 if (!this.store) {
48940                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48941                 }
48942                 var data = {};
48943                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48944                 this.store.proxy = new Roo.data.MemoryProxy(data);
48945                 this.store.load();
48946         },
48947
48948 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48949         getValue: function() {
48950                 var result = '(';
48951                 this.store.each(function(rec) {
48952                         result += rec.id + ',';
48953                 });
48954                 return result.substr(0, result.length - 1) + ')';
48955         },
48956         
48957         getIds: function() {
48958                 var i = 0, result = new Array(this.store.getCount());
48959                 this.store.each(function(rec) {
48960                         result[i++] = rec.id;
48961                 });
48962                 return result;
48963         },
48964         
48965         isDirty: function() {
48966                 return this.isDirtyFlag;
48967         },
48968
48969 /**
48970  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48971  *      whole Element becomes the target, and this causes the drop gesture to append.
48972  */
48973     getTargetFromEvent : function(e) {
48974                 var target = e.getTarget();
48975                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48976                 target = target.parentNode;
48977                 }
48978                 if (!target) {
48979                         target = this.el.dom.lastChild || this.el.dom;
48980                 }
48981                 return target;
48982     },
48983
48984 /**
48985  *      Create the drag data which consists of an object which has the property "ddel" as
48986  *      the drag proxy element. 
48987  */
48988     getDragData : function(e) {
48989         var target = this.findItemFromChild(e.getTarget());
48990                 if(target) {
48991                         this.handleSelection(e);
48992                         var selNodes = this.getSelectedNodes();
48993             var dragData = {
48994                 source: this,
48995                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48996                 nodes: selNodes,
48997                 records: []
48998                         };
48999                         var selectedIndices = this.getSelectedIndexes();
49000                         for (var i = 0; i < selectedIndices.length; i++) {
49001                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
49002                         }
49003                         if (selNodes.length == 1) {
49004                                 dragData.ddel = target.cloneNode(true); // the div element
49005                         } else {
49006                                 var div = document.createElement('div'); // create the multi element drag "ghost"
49007                                 div.className = 'multi-proxy';
49008                                 for (var i = 0, len = selNodes.length; i < len; i++) {
49009                                         div.appendChild(selNodes[i].cloneNode(true));
49010                                 }
49011                                 dragData.ddel = div;
49012                         }
49013             //console.log(dragData)
49014             //console.log(dragData.ddel.innerHTML)
49015                         return dragData;
49016                 }
49017         //console.log('nodragData')
49018                 return false;
49019     },
49020     
49021 /**     Specify to which ddGroup items in this DDView may be dragged. */
49022     setDraggable: function(ddGroup) {
49023         if (ddGroup instanceof Array) {
49024                 Roo.each(ddGroup, this.setDraggable, this);
49025                 return;
49026         }
49027         if (this.dragZone) {
49028                 this.dragZone.addToGroup(ddGroup);
49029         } else {
49030                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
49031                                 containerScroll: true,
49032                                 ddGroup: ddGroup 
49033
49034                         });
49035 //                      Draggability implies selection. DragZone's mousedown selects the element.
49036                         if (!this.multiSelect) { this.singleSelect = true; }
49037
49038 //                      Wire the DragZone's handlers up to methods in *this*
49039                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
49040                 }
49041     },
49042
49043 /**     Specify from which ddGroup this DDView accepts drops. */
49044     setDroppable: function(ddGroup) {
49045         if (ddGroup instanceof Array) {
49046                 Roo.each(ddGroup, this.setDroppable, this);
49047                 return;
49048         }
49049         if (this.dropZone) {
49050                 this.dropZone.addToGroup(ddGroup);
49051         } else {
49052                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
49053                                 containerScroll: true,
49054                                 ddGroup: ddGroup
49055                         });
49056
49057 //                      Wire the DropZone's handlers up to methods in *this*
49058                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
49059                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
49060                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
49061                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
49062                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
49063                 }
49064     },
49065
49066 /**     Decide whether to drop above or below a View node. */
49067     getDropPoint : function(e, n, dd){
49068         if (n == this.el.dom) { return "above"; }
49069                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
49070                 var c = t + (b - t) / 2;
49071                 var y = Roo.lib.Event.getPageY(e);
49072                 if(y <= c) {
49073                         return "above";
49074                 }else{
49075                         return "below";
49076                 }
49077     },
49078
49079     onNodeEnter : function(n, dd, e, data){
49080                 return false;
49081     },
49082     
49083     onNodeOver : function(n, dd, e, data){
49084                 var pt = this.getDropPoint(e, n, dd);
49085                 // set the insert point style on the target node
49086                 var dragElClass = this.dropNotAllowed;
49087                 if (pt) {
49088                         var targetElClass;
49089                         if (pt == "above"){
49090                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
49091                                 targetElClass = "x-view-drag-insert-above";
49092                         } else {
49093                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
49094                                 targetElClass = "x-view-drag-insert-below";
49095                         }
49096                         if (this.lastInsertClass != targetElClass){
49097                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
49098                                 this.lastInsertClass = targetElClass;
49099                         }
49100                 }
49101                 return dragElClass;
49102         },
49103
49104     onNodeOut : function(n, dd, e, data){
49105                 this.removeDropIndicators(n);
49106     },
49107
49108     onNodeDrop : function(n, dd, e, data){
49109         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
49110                 return false;
49111         }
49112         var pt = this.getDropPoint(e, n, dd);
49113                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
49114                 if (pt == "below") { insertAt++; }
49115                 for (var i = 0; i < data.records.length; i++) {
49116                         var r = data.records[i];
49117                         var dup = this.store.getById(r.id);
49118                         if (dup && (dd != this.dragZone)) {
49119                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
49120                         } else {
49121                                 if (data.copy) {
49122                                         this.store.insert(insertAt++, r.copy());
49123                                 } else {
49124                                         data.source.isDirtyFlag = true;
49125                                         r.store.remove(r);
49126                                         this.store.insert(insertAt++, r);
49127                                 }
49128                                 this.isDirtyFlag = true;
49129                         }
49130                 }
49131                 this.dragZone.cachedTarget = null;
49132                 return true;
49133     },
49134
49135     removeDropIndicators : function(n){
49136                 if(n){
49137                         Roo.fly(n).removeClass([
49138                                 "x-view-drag-insert-above",
49139                                 "x-view-drag-insert-below"]);
49140                         this.lastInsertClass = "_noclass";
49141                 }
49142     },
49143
49144 /**
49145  *      Utility method. Add a delete option to the DDView's context menu.
49146  *      @param {String} imageUrl The URL of the "delete" icon image.
49147  */
49148         setDeletable: function(imageUrl) {
49149                 if (!this.singleSelect && !this.multiSelect) {
49150                         this.singleSelect = true;
49151                 }
49152                 var c = this.getContextMenu();
49153                 this.contextMenu.on("itemclick", function(item) {
49154                         switch (item.id) {
49155                                 case "delete":
49156                                         this.remove(this.getSelectedIndexes());
49157                                         break;
49158                         }
49159                 }, this);
49160                 this.contextMenu.add({
49161                         icon: imageUrl,
49162                         id: "delete",
49163                         text: 'Delete'
49164                 });
49165         },
49166         
49167 /**     Return the context menu for this DDView. */
49168         getContextMenu: function() {
49169                 if (!this.contextMenu) {
49170 //                      Create the View's context menu
49171                         this.contextMenu = new Roo.menu.Menu({
49172                                 id: this.id + "-contextmenu"
49173                         });
49174                         this.el.on("contextmenu", this.showContextMenu, this);
49175                 }
49176                 return this.contextMenu;
49177         },
49178         
49179         disableContextMenu: function() {
49180                 if (this.contextMenu) {
49181                         this.el.un("contextmenu", this.showContextMenu, this);
49182                 }
49183         },
49184
49185         showContextMenu: function(e, item) {
49186         item = this.findItemFromChild(e.getTarget());
49187                 if (item) {
49188                         e.stopEvent();
49189                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49190                         this.contextMenu.showAt(e.getXY());
49191             }
49192     },
49193
49194 /**
49195  *      Remove {@link Roo.data.Record}s at the specified indices.
49196  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49197  */
49198     remove: function(selectedIndices) {
49199                 selectedIndices = [].concat(selectedIndices);
49200                 for (var i = 0; i < selectedIndices.length; i++) {
49201                         var rec = this.store.getAt(selectedIndices[i]);
49202                         this.store.remove(rec);
49203                 }
49204     },
49205
49206 /**
49207  *      Double click fires the event, but also, if this is draggable, and there is only one other
49208  *      related DropZone, it transfers the selected node.
49209  */
49210     onDblClick : function(e){
49211         var item = this.findItemFromChild(e.getTarget());
49212         if(item){
49213             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49214                 return false;
49215             }
49216             if (this.dragGroup) {
49217                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49218                     while (targets.indexOf(this.dropZone) > -1) {
49219                             targets.remove(this.dropZone);
49220                                 }
49221                     if (targets.length == 1) {
49222                                         this.dragZone.cachedTarget = null;
49223                         var el = Roo.get(targets[0].getEl());
49224                         var box = el.getBox(true);
49225                         targets[0].onNodeDrop(el.dom, {
49226                                 target: el.dom,
49227                                 xy: [box.x, box.y + box.height - 1]
49228                         }, null, this.getDragData(e));
49229                     }
49230                 }
49231         }
49232     },
49233     
49234     handleSelection: function(e) {
49235                 this.dragZone.cachedTarget = null;
49236         var item = this.findItemFromChild(e.getTarget());
49237         if (!item) {
49238                 this.clearSelections(true);
49239                 return;
49240         }
49241                 if (item && (this.multiSelect || this.singleSelect)){
49242                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49243                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49244                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49245                                 this.unselect(item);
49246                         } else {
49247                                 this.select(item, this.multiSelect && e.ctrlKey);
49248                                 this.lastSelection = item;
49249                         }
49250                 }
49251     },
49252
49253     onItemClick : function(item, index, e){
49254                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49255                         return false;
49256                 }
49257                 return true;
49258     },
49259
49260     unselect : function(nodeInfo, suppressEvent){
49261                 var node = this.getNode(nodeInfo);
49262                 if(node && this.isSelected(node)){
49263                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49264                                 Roo.fly(node).removeClass(this.selectedClass);
49265                                 this.selections.remove(node);
49266                                 if(!suppressEvent){
49267                                         this.fireEvent("selectionchange", this, this.selections);
49268                                 }
49269                         }
49270                 }
49271     }
49272 });
49273 /*
49274  * Based on:
49275  * Ext JS Library 1.1.1
49276  * Copyright(c) 2006-2007, Ext JS, LLC.
49277  *
49278  * Originally Released Under LGPL - original licence link has changed is not relivant.
49279  *
49280  * Fork - LGPL
49281  * <script type="text/javascript">
49282  */
49283  
49284 /**
49285  * @class Roo.LayoutManager
49286  * @extends Roo.util.Observable
49287  * Base class for layout managers.
49288  */
49289 Roo.LayoutManager = function(container, config){
49290     Roo.LayoutManager.superclass.constructor.call(this);
49291     this.el = Roo.get(container);
49292     // ie scrollbar fix
49293     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49294         document.body.scroll = "no";
49295     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49296         this.el.position('relative');
49297     }
49298     this.id = this.el.id;
49299     this.el.addClass("x-layout-container");
49300     /** false to disable window resize monitoring @type Boolean */
49301     this.monitorWindowResize = true;
49302     this.regions = {};
49303     this.addEvents({
49304         /**
49305          * @event layout
49306          * Fires when a layout is performed. 
49307          * @param {Roo.LayoutManager} this
49308          */
49309         "layout" : true,
49310         /**
49311          * @event regionresized
49312          * Fires when the user resizes a region. 
49313          * @param {Roo.LayoutRegion} region The resized region
49314          * @param {Number} newSize The new size (width for east/west, height for north/south)
49315          */
49316         "regionresized" : true,
49317         /**
49318          * @event regioncollapsed
49319          * Fires when a region is collapsed. 
49320          * @param {Roo.LayoutRegion} region The collapsed region
49321          */
49322         "regioncollapsed" : true,
49323         /**
49324          * @event regionexpanded
49325          * Fires when a region is expanded.  
49326          * @param {Roo.LayoutRegion} region The expanded region
49327          */
49328         "regionexpanded" : true
49329     });
49330     this.updating = false;
49331     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49332 };
49333
49334 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49335     /**
49336      * Returns true if this layout is currently being updated
49337      * @return {Boolean}
49338      */
49339     isUpdating : function(){
49340         return this.updating; 
49341     },
49342     
49343     /**
49344      * Suspend the LayoutManager from doing auto-layouts while
49345      * making multiple add or remove calls
49346      */
49347     beginUpdate : function(){
49348         this.updating = true;    
49349     },
49350     
49351     /**
49352      * Restore auto-layouts and optionally disable the manager from performing a layout
49353      * @param {Boolean} noLayout true to disable a layout update 
49354      */
49355     endUpdate : function(noLayout){
49356         this.updating = false;
49357         if(!noLayout){
49358             this.layout();
49359         }    
49360     },
49361     
49362     layout: function(){
49363         
49364     },
49365     
49366     onRegionResized : function(region, newSize){
49367         this.fireEvent("regionresized", region, newSize);
49368         this.layout();
49369     },
49370     
49371     onRegionCollapsed : function(region){
49372         this.fireEvent("regioncollapsed", region);
49373     },
49374     
49375     onRegionExpanded : function(region){
49376         this.fireEvent("regionexpanded", region);
49377     },
49378         
49379     /**
49380      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49381      * performs box-model adjustments.
49382      * @return {Object} The size as an object {width: (the width), height: (the height)}
49383      */
49384     getViewSize : function(){
49385         var size;
49386         if(this.el.dom != document.body){
49387             size = this.el.getSize();
49388         }else{
49389             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49390         }
49391         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49392         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49393         return size;
49394     },
49395     
49396     /**
49397      * Returns the Element this layout is bound to.
49398      * @return {Roo.Element}
49399      */
49400     getEl : function(){
49401         return this.el;
49402     },
49403     
49404     /**
49405      * Returns the specified region.
49406      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49407      * @return {Roo.LayoutRegion}
49408      */
49409     getRegion : function(target){
49410         return this.regions[target.toLowerCase()];
49411     },
49412     
49413     onWindowResize : function(){
49414         if(this.monitorWindowResize){
49415             this.layout();
49416         }
49417     }
49418 });/*
49419  * Based on:
49420  * Ext JS Library 1.1.1
49421  * Copyright(c) 2006-2007, Ext JS, LLC.
49422  *
49423  * Originally Released Under LGPL - original licence link has changed is not relivant.
49424  *
49425  * Fork - LGPL
49426  * <script type="text/javascript">
49427  */
49428 /**
49429  * @class Roo.BorderLayout
49430  * @extends Roo.LayoutManager
49431  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49432  * please see: <br><br>
49433  * <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>
49434  * <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>
49435  * Example:
49436  <pre><code>
49437  var layout = new Roo.BorderLayout(document.body, {
49438     north: {
49439         initialSize: 25,
49440         titlebar: false
49441     },
49442     west: {
49443         split:true,
49444         initialSize: 200,
49445         minSize: 175,
49446         maxSize: 400,
49447         titlebar: true,
49448         collapsible: true
49449     },
49450     east: {
49451         split:true,
49452         initialSize: 202,
49453         minSize: 175,
49454         maxSize: 400,
49455         titlebar: true,
49456         collapsible: true
49457     },
49458     south: {
49459         split:true,
49460         initialSize: 100,
49461         minSize: 100,
49462         maxSize: 200,
49463         titlebar: true,
49464         collapsible: true
49465     },
49466     center: {
49467         titlebar: true,
49468         autoScroll:true,
49469         resizeTabs: true,
49470         minTabWidth: 50,
49471         preferredTabWidth: 150
49472     }
49473 });
49474
49475 // shorthand
49476 var CP = Roo.ContentPanel;
49477
49478 layout.beginUpdate();
49479 layout.add("north", new CP("north", "North"));
49480 layout.add("south", new CP("south", {title: "South", closable: true}));
49481 layout.add("west", new CP("west", {title: "West"}));
49482 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49483 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49484 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49485 layout.getRegion("center").showPanel("center1");
49486 layout.endUpdate();
49487 </code></pre>
49488
49489 <b>The container the layout is rendered into can be either the body element or any other element.
49490 If it is not the body element, the container needs to either be an absolute positioned element,
49491 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49492 the container size if it is not the body element.</b>
49493
49494 * @constructor
49495 * Create a new BorderLayout
49496 * @param {String/HTMLElement/Element} container The container this layout is bound to
49497 * @param {Object} config Configuration options
49498  */
49499 Roo.BorderLayout = function(container, config){
49500     config = config || {};
49501     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49502     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49503     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49504         var target = this.factory.validRegions[i];
49505         if(config[target]){
49506             this.addRegion(target, config[target]);
49507         }
49508     }
49509 };
49510
49511 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49512     /**
49513      * Creates and adds a new region if it doesn't already exist.
49514      * @param {String} target The target region key (north, south, east, west or center).
49515      * @param {Object} config The regions config object
49516      * @return {BorderLayoutRegion} The new region
49517      */
49518     addRegion : function(target, config){
49519         if(!this.regions[target]){
49520             var r = this.factory.create(target, this, config);
49521             this.bindRegion(target, r);
49522         }
49523         return this.regions[target];
49524     },
49525
49526     // private (kinda)
49527     bindRegion : function(name, r){
49528         this.regions[name] = r;
49529         r.on("visibilitychange", this.layout, this);
49530         r.on("paneladded", this.layout, this);
49531         r.on("panelremoved", this.layout, this);
49532         r.on("invalidated", this.layout, this);
49533         r.on("resized", this.onRegionResized, this);
49534         r.on("collapsed", this.onRegionCollapsed, this);
49535         r.on("expanded", this.onRegionExpanded, this);
49536     },
49537
49538     /**
49539      * Performs a layout update.
49540      */
49541     layout : function(){
49542         if(this.updating) {
49543             return;
49544         }
49545         var size = this.getViewSize();
49546         var w = size.width;
49547         var h = size.height;
49548         var centerW = w;
49549         var centerH = h;
49550         var centerY = 0;
49551         var centerX = 0;
49552         //var x = 0, y = 0;
49553
49554         var rs = this.regions;
49555         var north = rs["north"];
49556         var south = rs["south"]; 
49557         var west = rs["west"];
49558         var east = rs["east"];
49559         var center = rs["center"];
49560         //if(this.hideOnLayout){ // not supported anymore
49561             //c.el.setStyle("display", "none");
49562         //}
49563         if(north && north.isVisible()){
49564             var b = north.getBox();
49565             var m = north.getMargins();
49566             b.width = w - (m.left+m.right);
49567             b.x = m.left;
49568             b.y = m.top;
49569             centerY = b.height + b.y + m.bottom;
49570             centerH -= centerY;
49571             north.updateBox(this.safeBox(b));
49572         }
49573         if(south && south.isVisible()){
49574             var b = south.getBox();
49575             var m = south.getMargins();
49576             b.width = w - (m.left+m.right);
49577             b.x = m.left;
49578             var totalHeight = (b.height + m.top + m.bottom);
49579             b.y = h - totalHeight + m.top;
49580             centerH -= totalHeight;
49581             south.updateBox(this.safeBox(b));
49582         }
49583         if(west && west.isVisible()){
49584             var b = west.getBox();
49585             var m = west.getMargins();
49586             b.height = centerH - (m.top+m.bottom);
49587             b.x = m.left;
49588             b.y = centerY + m.top;
49589             var totalWidth = (b.width + m.left + m.right);
49590             centerX += totalWidth;
49591             centerW -= totalWidth;
49592             west.updateBox(this.safeBox(b));
49593         }
49594         if(east && east.isVisible()){
49595             var b = east.getBox();
49596             var m = east.getMargins();
49597             b.height = centerH - (m.top+m.bottom);
49598             var totalWidth = (b.width + m.left + m.right);
49599             b.x = w - totalWidth + m.left;
49600             b.y = centerY + m.top;
49601             centerW -= totalWidth;
49602             east.updateBox(this.safeBox(b));
49603         }
49604         if(center){
49605             var m = center.getMargins();
49606             var centerBox = {
49607                 x: centerX + m.left,
49608                 y: centerY + m.top,
49609                 width: centerW - (m.left+m.right),
49610                 height: centerH - (m.top+m.bottom)
49611             };
49612             //if(this.hideOnLayout){
49613                 //center.el.setStyle("display", "block");
49614             //}
49615             center.updateBox(this.safeBox(centerBox));
49616         }
49617         this.el.repaint();
49618         this.fireEvent("layout", this);
49619     },
49620
49621     // private
49622     safeBox : function(box){
49623         box.width = Math.max(0, box.width);
49624         box.height = Math.max(0, box.height);
49625         return box;
49626     },
49627
49628     /**
49629      * Adds a ContentPanel (or subclass) to this layout.
49630      * @param {String} target The target region key (north, south, east, west or center).
49631      * @param {Roo.ContentPanel} panel The panel to add
49632      * @return {Roo.ContentPanel} The added panel
49633      */
49634     add : function(target, panel){
49635          
49636         target = target.toLowerCase();
49637         return this.regions[target].add(panel);
49638     },
49639
49640     /**
49641      * Remove a ContentPanel (or subclass) to this layout.
49642      * @param {String} target The target region key (north, south, east, west or center).
49643      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49644      * @return {Roo.ContentPanel} The removed panel
49645      */
49646     remove : function(target, panel){
49647         target = target.toLowerCase();
49648         return this.regions[target].remove(panel);
49649     },
49650
49651     /**
49652      * Searches all regions for a panel with the specified id
49653      * @param {String} panelId
49654      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49655      */
49656     findPanel : function(panelId){
49657         var rs = this.regions;
49658         for(var target in rs){
49659             if(typeof rs[target] != "function"){
49660                 var p = rs[target].getPanel(panelId);
49661                 if(p){
49662                     return p;
49663                 }
49664             }
49665         }
49666         return null;
49667     },
49668
49669     /**
49670      * Searches all regions for a panel with the specified id and activates (shows) it.
49671      * @param {String/ContentPanel} panelId The panels id or the panel itself
49672      * @return {Roo.ContentPanel} The shown panel or null
49673      */
49674     showPanel : function(panelId) {
49675       var rs = this.regions;
49676       for(var target in rs){
49677          var r = rs[target];
49678          if(typeof r != "function"){
49679             if(r.hasPanel(panelId)){
49680                return r.showPanel(panelId);
49681             }
49682          }
49683       }
49684       return null;
49685    },
49686
49687    /**
49688      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49689      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49690      */
49691     restoreState : function(provider){
49692         if(!provider){
49693             provider = Roo.state.Manager;
49694         }
49695         var sm = new Roo.LayoutStateManager();
49696         sm.init(this, provider);
49697     },
49698
49699     /**
49700      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49701      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49702      * a valid ContentPanel config object.  Example:
49703      * <pre><code>
49704 // Create the main layout
49705 var layout = new Roo.BorderLayout('main-ct', {
49706     west: {
49707         split:true,
49708         minSize: 175,
49709         titlebar: true
49710     },
49711     center: {
49712         title:'Components'
49713     }
49714 }, 'main-ct');
49715
49716 // Create and add multiple ContentPanels at once via configs
49717 layout.batchAdd({
49718    west: {
49719        id: 'source-files',
49720        autoCreate:true,
49721        title:'Ext Source Files',
49722        autoScroll:true,
49723        fitToFrame:true
49724    },
49725    center : {
49726        el: cview,
49727        autoScroll:true,
49728        fitToFrame:true,
49729        toolbar: tb,
49730        resizeEl:'cbody'
49731    }
49732 });
49733 </code></pre>
49734      * @param {Object} regions An object containing ContentPanel configs by region name
49735      */
49736     batchAdd : function(regions){
49737         this.beginUpdate();
49738         for(var rname in regions){
49739             var lr = this.regions[rname];
49740             if(lr){
49741                 this.addTypedPanels(lr, regions[rname]);
49742             }
49743         }
49744         this.endUpdate();
49745     },
49746
49747     // private
49748     addTypedPanels : function(lr, ps){
49749         if(typeof ps == 'string'){
49750             lr.add(new Roo.ContentPanel(ps));
49751         }
49752         else if(ps instanceof Array){
49753             for(var i =0, len = ps.length; i < len; i++){
49754                 this.addTypedPanels(lr, ps[i]);
49755             }
49756         }
49757         else if(!ps.events){ // raw config?
49758             var el = ps.el;
49759             delete ps.el; // prevent conflict
49760             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49761         }
49762         else {  // panel object assumed!
49763             lr.add(ps);
49764         }
49765     },
49766     /**
49767      * Adds a xtype elements to the layout.
49768      * <pre><code>
49769
49770 layout.addxtype({
49771        xtype : 'ContentPanel',
49772        region: 'west',
49773        items: [ .... ]
49774    }
49775 );
49776
49777 layout.addxtype({
49778         xtype : 'NestedLayoutPanel',
49779         region: 'west',
49780         layout: {
49781            center: { },
49782            west: { }   
49783         },
49784         items : [ ... list of content panels or nested layout panels.. ]
49785    }
49786 );
49787 </code></pre>
49788      * @param {Object} cfg Xtype definition of item to add.
49789      */
49790     addxtype : function(cfg)
49791     {
49792         // basically accepts a pannel...
49793         // can accept a layout region..!?!?
49794         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49795         
49796         if (!cfg.xtype.match(/Panel$/)) {
49797             return false;
49798         }
49799         var ret = false;
49800         
49801         if (typeof(cfg.region) == 'undefined') {
49802             Roo.log("Failed to add Panel, region was not set");
49803             Roo.log(cfg);
49804             return false;
49805         }
49806         var region = cfg.region;
49807         delete cfg.region;
49808         
49809           
49810         var xitems = [];
49811         if (cfg.items) {
49812             xitems = cfg.items;
49813             delete cfg.items;
49814         }
49815         var nb = false;
49816         
49817         switch(cfg.xtype) 
49818         {
49819             case 'ContentPanel':  // ContentPanel (el, cfg)
49820             case 'ScrollPanel':  // ContentPanel (el, cfg)
49821             case 'ViewPanel': 
49822                 if(cfg.autoCreate) {
49823                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49824                 } else {
49825                     var el = this.el.createChild();
49826                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49827                 }
49828                 
49829                 this.add(region, ret);
49830                 break;
49831             
49832             
49833             case 'TreePanel': // our new panel!
49834                 cfg.el = this.el.createChild();
49835                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49836                 this.add(region, ret);
49837                 break;
49838             
49839             case 'NestedLayoutPanel': 
49840                 // create a new Layout (which is  a Border Layout...
49841                 var el = this.el.createChild();
49842                 var clayout = cfg.layout;
49843                 delete cfg.layout;
49844                 clayout.items   = clayout.items  || [];
49845                 // replace this exitems with the clayout ones..
49846                 xitems = clayout.items;
49847                  
49848                 
49849                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49850                     cfg.background = false;
49851                 }
49852                 var layout = new Roo.BorderLayout(el, clayout);
49853                 
49854                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49855                 //console.log('adding nested layout panel '  + cfg.toSource());
49856                 this.add(region, ret);
49857                 nb = {}; /// find first...
49858                 break;
49859                 
49860             case 'GridPanel': 
49861             
49862                 // needs grid and region
49863                 
49864                 //var el = this.getRegion(region).el.createChild();
49865                 var el = this.el.createChild();
49866                 // create the grid first...
49867                 
49868                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49869                 delete cfg.grid;
49870                 if (region == 'center' && this.active ) {
49871                     cfg.background = false;
49872                 }
49873                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49874                 
49875                 this.add(region, ret);
49876                 if (cfg.background) {
49877                     ret.on('activate', function(gp) {
49878                         if (!gp.grid.rendered) {
49879                             gp.grid.render();
49880                         }
49881                     });
49882                 } else {
49883                     grid.render();
49884                 }
49885                 break;
49886            
49887            
49888            
49889                 
49890                 
49891                 
49892             default:
49893                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49894                     
49895                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49896                     this.add(region, ret);
49897                 } else {
49898                 
49899                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49900                     return null;
49901                 }
49902                 
49903              // GridPanel (grid, cfg)
49904             
49905         }
49906         this.beginUpdate();
49907         // add children..
49908         var region = '';
49909         var abn = {};
49910         Roo.each(xitems, function(i)  {
49911             region = nb && i.region ? i.region : false;
49912             
49913             var add = ret.addxtype(i);
49914            
49915             if (region) {
49916                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49917                 if (!i.background) {
49918                     abn[region] = nb[region] ;
49919                 }
49920             }
49921             
49922         });
49923         this.endUpdate();
49924
49925         // make the last non-background panel active..
49926         //if (nb) { Roo.log(abn); }
49927         if (nb) {
49928             
49929             for(var r in abn) {
49930                 region = this.getRegion(r);
49931                 if (region) {
49932                     // tried using nb[r], but it does not work..
49933                      
49934                     region.showPanel(abn[r]);
49935                    
49936                 }
49937             }
49938         }
49939         return ret;
49940         
49941     }
49942 });
49943
49944 /**
49945  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49946  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49947  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49948  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49949  * <pre><code>
49950 // shorthand
49951 var CP = Roo.ContentPanel;
49952
49953 var layout = Roo.BorderLayout.create({
49954     north: {
49955         initialSize: 25,
49956         titlebar: false,
49957         panels: [new CP("north", "North")]
49958     },
49959     west: {
49960         split:true,
49961         initialSize: 200,
49962         minSize: 175,
49963         maxSize: 400,
49964         titlebar: true,
49965         collapsible: true,
49966         panels: [new CP("west", {title: "West"})]
49967     },
49968     east: {
49969         split:true,
49970         initialSize: 202,
49971         minSize: 175,
49972         maxSize: 400,
49973         titlebar: true,
49974         collapsible: true,
49975         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49976     },
49977     south: {
49978         split:true,
49979         initialSize: 100,
49980         minSize: 100,
49981         maxSize: 200,
49982         titlebar: true,
49983         collapsible: true,
49984         panels: [new CP("south", {title: "South", closable: true})]
49985     },
49986     center: {
49987         titlebar: true,
49988         autoScroll:true,
49989         resizeTabs: true,
49990         minTabWidth: 50,
49991         preferredTabWidth: 150,
49992         panels: [
49993             new CP("center1", {title: "Close Me", closable: true}),
49994             new CP("center2", {title: "Center Panel", closable: false})
49995         ]
49996     }
49997 }, document.body);
49998
49999 layout.getRegion("center").showPanel("center1");
50000 </code></pre>
50001  * @param config
50002  * @param targetEl
50003  */
50004 Roo.BorderLayout.create = function(config, targetEl){
50005     var layout = new Roo.BorderLayout(targetEl || document.body, config);
50006     layout.beginUpdate();
50007     var regions = Roo.BorderLayout.RegionFactory.validRegions;
50008     for(var j = 0, jlen = regions.length; j < jlen; j++){
50009         var lr = regions[j];
50010         if(layout.regions[lr] && config[lr].panels){
50011             var r = layout.regions[lr];
50012             var ps = config[lr].panels;
50013             layout.addTypedPanels(r, ps);
50014         }
50015     }
50016     layout.endUpdate();
50017     return layout;
50018 };
50019
50020 // private
50021 Roo.BorderLayout.RegionFactory = {
50022     // private
50023     validRegions : ["north","south","east","west","center"],
50024
50025     // private
50026     create : function(target, mgr, config){
50027         target = target.toLowerCase();
50028         if(config.lightweight || config.basic){
50029             return new Roo.BasicLayoutRegion(mgr, config, target);
50030         }
50031         switch(target){
50032             case "north":
50033                 return new Roo.NorthLayoutRegion(mgr, config);
50034             case "south":
50035                 return new Roo.SouthLayoutRegion(mgr, config);
50036             case "east":
50037                 return new Roo.EastLayoutRegion(mgr, config);
50038             case "west":
50039                 return new Roo.WestLayoutRegion(mgr, config);
50040             case "center":
50041                 return new Roo.CenterLayoutRegion(mgr, config);
50042         }
50043         throw 'Layout region "'+target+'" not supported.';
50044     }
50045 };/*
50046  * Based on:
50047  * Ext JS Library 1.1.1
50048  * Copyright(c) 2006-2007, Ext JS, LLC.
50049  *
50050  * Originally Released Under LGPL - original licence link has changed is not relivant.
50051  *
50052  * Fork - LGPL
50053  * <script type="text/javascript">
50054  */
50055  
50056 /**
50057  * @class Roo.BasicLayoutRegion
50058  * @extends Roo.util.Observable
50059  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
50060  * and does not have a titlebar, tabs or any other features. All it does is size and position 
50061  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
50062  */
50063 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
50064     this.mgr = mgr;
50065     this.position  = pos;
50066     this.events = {
50067         /**
50068          * @scope Roo.BasicLayoutRegion
50069          */
50070         
50071         /**
50072          * @event beforeremove
50073          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
50074          * @param {Roo.LayoutRegion} this
50075          * @param {Roo.ContentPanel} panel The panel
50076          * @param {Object} e The cancel event object
50077          */
50078         "beforeremove" : true,
50079         /**
50080          * @event invalidated
50081          * Fires when the layout for this region is changed.
50082          * @param {Roo.LayoutRegion} this
50083          */
50084         "invalidated" : true,
50085         /**
50086          * @event visibilitychange
50087          * Fires when this region is shown or hidden 
50088          * @param {Roo.LayoutRegion} this
50089          * @param {Boolean} visibility true or false
50090          */
50091         "visibilitychange" : true,
50092         /**
50093          * @event paneladded
50094          * Fires when a panel is added. 
50095          * @param {Roo.LayoutRegion} this
50096          * @param {Roo.ContentPanel} panel The panel
50097          */
50098         "paneladded" : true,
50099         /**
50100          * @event panelremoved
50101          * Fires when a panel is removed. 
50102          * @param {Roo.LayoutRegion} this
50103          * @param {Roo.ContentPanel} panel The panel
50104          */
50105         "panelremoved" : true,
50106         /**
50107          * @event collapsed
50108          * Fires when this region is collapsed.
50109          * @param {Roo.LayoutRegion} this
50110          */
50111         "collapsed" : true,
50112         /**
50113          * @event expanded
50114          * Fires when this region is expanded.
50115          * @param {Roo.LayoutRegion} this
50116          */
50117         "expanded" : true,
50118         /**
50119          * @event slideshow
50120          * Fires when this region is slid into view.
50121          * @param {Roo.LayoutRegion} this
50122          */
50123         "slideshow" : true,
50124         /**
50125          * @event slidehide
50126          * Fires when this region slides out of view. 
50127          * @param {Roo.LayoutRegion} this
50128          */
50129         "slidehide" : true,
50130         /**
50131          * @event panelactivated
50132          * Fires when a panel is activated. 
50133          * @param {Roo.LayoutRegion} this
50134          * @param {Roo.ContentPanel} panel The activated panel
50135          */
50136         "panelactivated" : true,
50137         /**
50138          * @event resized
50139          * Fires when the user resizes this region. 
50140          * @param {Roo.LayoutRegion} this
50141          * @param {Number} newSize The new size (width for east/west, height for north/south)
50142          */
50143         "resized" : true
50144     };
50145     /** A collection of panels in this region. @type Roo.util.MixedCollection */
50146     this.panels = new Roo.util.MixedCollection();
50147     this.panels.getKey = this.getPanelId.createDelegate(this);
50148     this.box = null;
50149     this.activePanel = null;
50150     // ensure listeners are added...
50151     
50152     if (config.listeners || config.events) {
50153         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50154             listeners : config.listeners || {},
50155             events : config.events || {}
50156         });
50157     }
50158     
50159     if(skipConfig !== true){
50160         this.applyConfig(config);
50161     }
50162 };
50163
50164 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50165     getPanelId : function(p){
50166         return p.getId();
50167     },
50168     
50169     applyConfig : function(config){
50170         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50171         this.config = config;
50172         
50173     },
50174     
50175     /**
50176      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50177      * the width, for horizontal (north, south) the height.
50178      * @param {Number} newSize The new width or height
50179      */
50180     resizeTo : function(newSize){
50181         var el = this.el ? this.el :
50182                  (this.activePanel ? this.activePanel.getEl() : null);
50183         if(el){
50184             switch(this.position){
50185                 case "east":
50186                 case "west":
50187                     el.setWidth(newSize);
50188                     this.fireEvent("resized", this, newSize);
50189                 break;
50190                 case "north":
50191                 case "south":
50192                     el.setHeight(newSize);
50193                     this.fireEvent("resized", this, newSize);
50194                 break;                
50195             }
50196         }
50197     },
50198     
50199     getBox : function(){
50200         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50201     },
50202     
50203     getMargins : function(){
50204         return this.margins;
50205     },
50206     
50207     updateBox : function(box){
50208         this.box = box;
50209         var el = this.activePanel.getEl();
50210         el.dom.style.left = box.x + "px";
50211         el.dom.style.top = box.y + "px";
50212         this.activePanel.setSize(box.width, box.height);
50213     },
50214     
50215     /**
50216      * Returns the container element for this region.
50217      * @return {Roo.Element}
50218      */
50219     getEl : function(){
50220         return this.activePanel;
50221     },
50222     
50223     /**
50224      * Returns true if this region is currently visible.
50225      * @return {Boolean}
50226      */
50227     isVisible : function(){
50228         return this.activePanel ? true : false;
50229     },
50230     
50231     setActivePanel : function(panel){
50232         panel = this.getPanel(panel);
50233         if(this.activePanel && this.activePanel != panel){
50234             this.activePanel.setActiveState(false);
50235             this.activePanel.getEl().setLeftTop(-10000,-10000);
50236         }
50237         this.activePanel = panel;
50238         panel.setActiveState(true);
50239         if(this.box){
50240             panel.setSize(this.box.width, this.box.height);
50241         }
50242         this.fireEvent("panelactivated", this, panel);
50243         this.fireEvent("invalidated");
50244     },
50245     
50246     /**
50247      * Show the specified panel.
50248      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50249      * @return {Roo.ContentPanel} The shown panel or null
50250      */
50251     showPanel : function(panel){
50252         if(panel = this.getPanel(panel)){
50253             this.setActivePanel(panel);
50254         }
50255         return panel;
50256     },
50257     
50258     /**
50259      * Get the active panel for this region.
50260      * @return {Roo.ContentPanel} The active panel or null
50261      */
50262     getActivePanel : function(){
50263         return this.activePanel;
50264     },
50265     
50266     /**
50267      * Add the passed ContentPanel(s)
50268      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50269      * @return {Roo.ContentPanel} The panel added (if only one was added)
50270      */
50271     add : function(panel){
50272         if(arguments.length > 1){
50273             for(var i = 0, len = arguments.length; i < len; i++) {
50274                 this.add(arguments[i]);
50275             }
50276             return null;
50277         }
50278         if(this.hasPanel(panel)){
50279             this.showPanel(panel);
50280             return panel;
50281         }
50282         var el = panel.getEl();
50283         if(el.dom.parentNode != this.mgr.el.dom){
50284             this.mgr.el.dom.appendChild(el.dom);
50285         }
50286         if(panel.setRegion){
50287             panel.setRegion(this);
50288         }
50289         this.panels.add(panel);
50290         el.setStyle("position", "absolute");
50291         if(!panel.background){
50292             this.setActivePanel(panel);
50293             if(this.config.initialSize && this.panels.getCount()==1){
50294                 this.resizeTo(this.config.initialSize);
50295             }
50296         }
50297         this.fireEvent("paneladded", this, panel);
50298         return panel;
50299     },
50300     
50301     /**
50302      * Returns true if the panel is in this region.
50303      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50304      * @return {Boolean}
50305      */
50306     hasPanel : function(panel){
50307         if(typeof panel == "object"){ // must be panel obj
50308             panel = panel.getId();
50309         }
50310         return this.getPanel(panel) ? true : false;
50311     },
50312     
50313     /**
50314      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50315      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50316      * @param {Boolean} preservePanel Overrides the config preservePanel option
50317      * @return {Roo.ContentPanel} The panel that was removed
50318      */
50319     remove : function(panel, preservePanel){
50320         panel = this.getPanel(panel);
50321         if(!panel){
50322             return null;
50323         }
50324         var e = {};
50325         this.fireEvent("beforeremove", this, panel, e);
50326         if(e.cancel === true){
50327             return null;
50328         }
50329         var panelId = panel.getId();
50330         this.panels.removeKey(panelId);
50331         return panel;
50332     },
50333     
50334     /**
50335      * Returns the panel specified or null if it's not in this region.
50336      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50337      * @return {Roo.ContentPanel}
50338      */
50339     getPanel : function(id){
50340         if(typeof id == "object"){ // must be panel obj
50341             return id;
50342         }
50343         return this.panels.get(id);
50344     },
50345     
50346     /**
50347      * Returns this regions position (north/south/east/west/center).
50348      * @return {String} 
50349      */
50350     getPosition: function(){
50351         return this.position;    
50352     }
50353 });/*
50354  * Based on:
50355  * Ext JS Library 1.1.1
50356  * Copyright(c) 2006-2007, Ext JS, LLC.
50357  *
50358  * Originally Released Under LGPL - original licence link has changed is not relivant.
50359  *
50360  * Fork - LGPL
50361  * <script type="text/javascript">
50362  */
50363  
50364 /**
50365  * @class Roo.LayoutRegion
50366  * @extends Roo.BasicLayoutRegion
50367  * This class represents a region in a layout manager.
50368  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50369  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50370  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50371  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50372  * @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})
50373  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50374  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50375  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50376  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50377  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50378  * @cfg {String}    title           The title for the region (overrides panel titles)
50379  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50380  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50381  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50382  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50383  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50384  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50385  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50386  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50387  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50388  * @cfg {Boolean}   showPin         True to show a pin button
50389  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50390  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50391  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50392  * @cfg {Number}    width           For East/West panels
50393  * @cfg {Number}    height          For North/South panels
50394  * @cfg {Boolean}   split           To show the splitter
50395  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50396  */
50397 Roo.LayoutRegion = function(mgr, config, pos){
50398     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50399     var dh = Roo.DomHelper;
50400     /** This region's container element 
50401     * @type Roo.Element */
50402     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50403     /** This region's title element 
50404     * @type Roo.Element */
50405
50406     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50407         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50408         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50409     ]}, true);
50410     this.titleEl.enableDisplayMode();
50411     /** This region's title text element 
50412     * @type HTMLElement */
50413     this.titleTextEl = this.titleEl.dom.firstChild;
50414     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50415     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50416     this.closeBtn.enableDisplayMode();
50417     this.closeBtn.on("click", this.closeClicked, this);
50418     this.closeBtn.hide();
50419
50420     this.createBody(config);
50421     this.visible = true;
50422     this.collapsed = false;
50423
50424     if(config.hideWhenEmpty){
50425         this.hide();
50426         this.on("paneladded", this.validateVisibility, this);
50427         this.on("panelremoved", this.validateVisibility, this);
50428     }
50429     this.applyConfig(config);
50430 };
50431
50432 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50433
50434     createBody : function(){
50435         /** This region's body element 
50436         * @type Roo.Element */
50437         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50438     },
50439
50440     applyConfig : function(c){
50441         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50442             var dh = Roo.DomHelper;
50443             if(c.titlebar !== false){
50444                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50445                 this.collapseBtn.on("click", this.collapse, this);
50446                 this.collapseBtn.enableDisplayMode();
50447
50448                 if(c.showPin === true || this.showPin){
50449                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50450                     this.stickBtn.enableDisplayMode();
50451                     this.stickBtn.on("click", this.expand, this);
50452                     this.stickBtn.hide();
50453                 }
50454             }
50455             /** This region's collapsed element
50456             * @type Roo.Element */
50457             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50458                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50459             ]}, true);
50460             if(c.floatable !== false){
50461                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50462                this.collapsedEl.on("click", this.collapseClick, this);
50463             }
50464
50465             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50466                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50467                    id: "message", unselectable: "on", style:{"float":"left"}});
50468                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50469              }
50470             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50471             this.expandBtn.on("click", this.expand, this);
50472         }
50473         if(this.collapseBtn){
50474             this.collapseBtn.setVisible(c.collapsible == true);
50475         }
50476         this.cmargins = c.cmargins || this.cmargins ||
50477                          (this.position == "west" || this.position == "east" ?
50478                              {top: 0, left: 2, right:2, bottom: 0} :
50479                              {top: 2, left: 0, right:0, bottom: 2});
50480         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50481         this.bottomTabs = c.tabPosition != "top";
50482         this.autoScroll = c.autoScroll || false;
50483         if(this.autoScroll){
50484             this.bodyEl.setStyle("overflow", "auto");
50485         }else{
50486             this.bodyEl.setStyle("overflow", "hidden");
50487         }
50488         //if(c.titlebar !== false){
50489             if((!c.titlebar && !c.title) || c.titlebar === false){
50490                 this.titleEl.hide();
50491             }else{
50492                 this.titleEl.show();
50493                 if(c.title){
50494                     this.titleTextEl.innerHTML = c.title;
50495                 }
50496             }
50497         //}
50498         this.duration = c.duration || .30;
50499         this.slideDuration = c.slideDuration || .45;
50500         this.config = c;
50501         if(c.collapsed){
50502             this.collapse(true);
50503         }
50504         if(c.hidden){
50505             this.hide();
50506         }
50507     },
50508     /**
50509      * Returns true if this region is currently visible.
50510      * @return {Boolean}
50511      */
50512     isVisible : function(){
50513         return this.visible;
50514     },
50515
50516     /**
50517      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50518      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50519      */
50520     setCollapsedTitle : function(title){
50521         title = title || "&#160;";
50522         if(this.collapsedTitleTextEl){
50523             this.collapsedTitleTextEl.innerHTML = title;
50524         }
50525     },
50526
50527     getBox : function(){
50528         var b;
50529         if(!this.collapsed){
50530             b = this.el.getBox(false, true);
50531         }else{
50532             b = this.collapsedEl.getBox(false, true);
50533         }
50534         return b;
50535     },
50536
50537     getMargins : function(){
50538         return this.collapsed ? this.cmargins : this.margins;
50539     },
50540
50541     highlight : function(){
50542         this.el.addClass("x-layout-panel-dragover");
50543     },
50544
50545     unhighlight : function(){
50546         this.el.removeClass("x-layout-panel-dragover");
50547     },
50548
50549     updateBox : function(box){
50550         this.box = box;
50551         if(!this.collapsed){
50552             this.el.dom.style.left = box.x + "px";
50553             this.el.dom.style.top = box.y + "px";
50554             this.updateBody(box.width, box.height);
50555         }else{
50556             this.collapsedEl.dom.style.left = box.x + "px";
50557             this.collapsedEl.dom.style.top = box.y + "px";
50558             this.collapsedEl.setSize(box.width, box.height);
50559         }
50560         if(this.tabs){
50561             this.tabs.autoSizeTabs();
50562         }
50563     },
50564
50565     updateBody : function(w, h){
50566         if(w !== null){
50567             this.el.setWidth(w);
50568             w -= this.el.getBorderWidth("rl");
50569             if(this.config.adjustments){
50570                 w += this.config.adjustments[0];
50571             }
50572         }
50573         if(h !== null){
50574             this.el.setHeight(h);
50575             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50576             h -= this.el.getBorderWidth("tb");
50577             if(this.config.adjustments){
50578                 h += this.config.adjustments[1];
50579             }
50580             this.bodyEl.setHeight(h);
50581             if(this.tabs){
50582                 h = this.tabs.syncHeight(h);
50583             }
50584         }
50585         if(this.panelSize){
50586             w = w !== null ? w : this.panelSize.width;
50587             h = h !== null ? h : this.panelSize.height;
50588         }
50589         if(this.activePanel){
50590             var el = this.activePanel.getEl();
50591             w = w !== null ? w : el.getWidth();
50592             h = h !== null ? h : el.getHeight();
50593             this.panelSize = {width: w, height: h};
50594             this.activePanel.setSize(w, h);
50595         }
50596         if(Roo.isIE && this.tabs){
50597             this.tabs.el.repaint();
50598         }
50599     },
50600
50601     /**
50602      * Returns the container element for this region.
50603      * @return {Roo.Element}
50604      */
50605     getEl : function(){
50606         return this.el;
50607     },
50608
50609     /**
50610      * Hides this region.
50611      */
50612     hide : function(){
50613         if(!this.collapsed){
50614             this.el.dom.style.left = "-2000px";
50615             this.el.hide();
50616         }else{
50617             this.collapsedEl.dom.style.left = "-2000px";
50618             this.collapsedEl.hide();
50619         }
50620         this.visible = false;
50621         this.fireEvent("visibilitychange", this, false);
50622     },
50623
50624     /**
50625      * Shows this region if it was previously hidden.
50626      */
50627     show : function(){
50628         if(!this.collapsed){
50629             this.el.show();
50630         }else{
50631             this.collapsedEl.show();
50632         }
50633         this.visible = true;
50634         this.fireEvent("visibilitychange", this, true);
50635     },
50636
50637     closeClicked : function(){
50638         if(this.activePanel){
50639             this.remove(this.activePanel);
50640         }
50641     },
50642
50643     collapseClick : function(e){
50644         if(this.isSlid){
50645            e.stopPropagation();
50646            this.slideIn();
50647         }else{
50648            e.stopPropagation();
50649            this.slideOut();
50650         }
50651     },
50652
50653     /**
50654      * Collapses this region.
50655      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50656      */
50657     collapse : function(skipAnim){
50658         if(this.collapsed) {
50659             return;
50660         }
50661         this.collapsed = true;
50662         if(this.split){
50663             this.split.el.hide();
50664         }
50665         if(this.config.animate && skipAnim !== true){
50666             this.fireEvent("invalidated", this);
50667             this.animateCollapse();
50668         }else{
50669             this.el.setLocation(-20000,-20000);
50670             this.el.hide();
50671             this.collapsedEl.show();
50672             this.fireEvent("collapsed", this);
50673             this.fireEvent("invalidated", this);
50674         }
50675     },
50676
50677     animateCollapse : function(){
50678         // overridden
50679     },
50680
50681     /**
50682      * Expands this region if it was previously collapsed.
50683      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50684      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50685      */
50686     expand : function(e, skipAnim){
50687         if(e) {
50688             e.stopPropagation();
50689         }
50690         if(!this.collapsed || this.el.hasActiveFx()) {
50691             return;
50692         }
50693         if(this.isSlid){
50694             this.afterSlideIn();
50695             skipAnim = true;
50696         }
50697         this.collapsed = false;
50698         if(this.config.animate && skipAnim !== true){
50699             this.animateExpand();
50700         }else{
50701             this.el.show();
50702             if(this.split){
50703                 this.split.el.show();
50704             }
50705             this.collapsedEl.setLocation(-2000,-2000);
50706             this.collapsedEl.hide();
50707             this.fireEvent("invalidated", this);
50708             this.fireEvent("expanded", this);
50709         }
50710     },
50711
50712     animateExpand : function(){
50713         // overridden
50714     },
50715
50716     initTabs : function()
50717     {
50718         this.bodyEl.setStyle("overflow", "hidden");
50719         var ts = new Roo.TabPanel(
50720                 this.bodyEl.dom,
50721                 {
50722                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50723                     disableTooltips: this.config.disableTabTips,
50724                     toolbar : this.config.toolbar
50725                 }
50726         );
50727         if(this.config.hideTabs){
50728             ts.stripWrap.setDisplayed(false);
50729         }
50730         this.tabs = ts;
50731         ts.resizeTabs = this.config.resizeTabs === true;
50732         ts.minTabWidth = this.config.minTabWidth || 40;
50733         ts.maxTabWidth = this.config.maxTabWidth || 250;
50734         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50735         ts.monitorResize = false;
50736         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50737         ts.bodyEl.addClass('x-layout-tabs-body');
50738         this.panels.each(this.initPanelAsTab, this);
50739     },
50740
50741     initPanelAsTab : function(panel){
50742         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50743                     this.config.closeOnTab && panel.isClosable());
50744         if(panel.tabTip !== undefined){
50745             ti.setTooltip(panel.tabTip);
50746         }
50747         ti.on("activate", function(){
50748               this.setActivePanel(panel);
50749         }, this);
50750         if(this.config.closeOnTab){
50751             ti.on("beforeclose", function(t, e){
50752                 e.cancel = true;
50753                 this.remove(panel);
50754             }, this);
50755         }
50756         return ti;
50757     },
50758
50759     updatePanelTitle : function(panel, title){
50760         if(this.activePanel == panel){
50761             this.updateTitle(title);
50762         }
50763         if(this.tabs){
50764             var ti = this.tabs.getTab(panel.getEl().id);
50765             ti.setText(title);
50766             if(panel.tabTip !== undefined){
50767                 ti.setTooltip(panel.tabTip);
50768             }
50769         }
50770     },
50771
50772     updateTitle : function(title){
50773         if(this.titleTextEl && !this.config.title){
50774             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50775         }
50776     },
50777
50778     setActivePanel : function(panel){
50779         panel = this.getPanel(panel);
50780         if(this.activePanel && this.activePanel != panel){
50781             this.activePanel.setActiveState(false);
50782         }
50783         this.activePanel = panel;
50784         panel.setActiveState(true);
50785         if(this.panelSize){
50786             panel.setSize(this.panelSize.width, this.panelSize.height);
50787         }
50788         if(this.closeBtn){
50789             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50790         }
50791         this.updateTitle(panel.getTitle());
50792         if(this.tabs){
50793             this.fireEvent("invalidated", this);
50794         }
50795         this.fireEvent("panelactivated", this, panel);
50796     },
50797
50798     /**
50799      * Shows the specified panel.
50800      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50801      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50802      */
50803     showPanel : function(panel)
50804     {
50805         panel = this.getPanel(panel);
50806         if(panel){
50807             if(this.tabs){
50808                 var tab = this.tabs.getTab(panel.getEl().id);
50809                 if(tab.isHidden()){
50810                     this.tabs.unhideTab(tab.id);
50811                 }
50812                 tab.activate();
50813             }else{
50814                 this.setActivePanel(panel);
50815             }
50816         }
50817         return panel;
50818     },
50819
50820     /**
50821      * Get the active panel for this region.
50822      * @return {Roo.ContentPanel} The active panel or null
50823      */
50824     getActivePanel : function(){
50825         return this.activePanel;
50826     },
50827
50828     validateVisibility : function(){
50829         if(this.panels.getCount() < 1){
50830             this.updateTitle("&#160;");
50831             this.closeBtn.hide();
50832             this.hide();
50833         }else{
50834             if(!this.isVisible()){
50835                 this.show();
50836             }
50837         }
50838     },
50839
50840     /**
50841      * Adds the passed ContentPanel(s) to this region.
50842      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50843      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50844      */
50845     add : function(panel){
50846         if(arguments.length > 1){
50847             for(var i = 0, len = arguments.length; i < len; i++) {
50848                 this.add(arguments[i]);
50849             }
50850             return null;
50851         }
50852         if(this.hasPanel(panel)){
50853             this.showPanel(panel);
50854             return panel;
50855         }
50856         panel.setRegion(this);
50857         this.panels.add(panel);
50858         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50859             this.bodyEl.dom.appendChild(panel.getEl().dom);
50860             if(panel.background !== true){
50861                 this.setActivePanel(panel);
50862             }
50863             this.fireEvent("paneladded", this, panel);
50864             return panel;
50865         }
50866         if(!this.tabs){
50867             this.initTabs();
50868         }else{
50869             this.initPanelAsTab(panel);
50870         }
50871         if(panel.background !== true){
50872             this.tabs.activate(panel.getEl().id);
50873         }
50874         this.fireEvent("paneladded", this, panel);
50875         return panel;
50876     },
50877
50878     /**
50879      * Hides the tab for the specified panel.
50880      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50881      */
50882     hidePanel : function(panel){
50883         if(this.tabs && (panel = this.getPanel(panel))){
50884             this.tabs.hideTab(panel.getEl().id);
50885         }
50886     },
50887
50888     /**
50889      * Unhides the tab for a previously hidden panel.
50890      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50891      */
50892     unhidePanel : function(panel){
50893         if(this.tabs && (panel = this.getPanel(panel))){
50894             this.tabs.unhideTab(panel.getEl().id);
50895         }
50896     },
50897
50898     clearPanels : function(){
50899         while(this.panels.getCount() > 0){
50900              this.remove(this.panels.first());
50901         }
50902     },
50903
50904     /**
50905      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50906      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50907      * @param {Boolean} preservePanel Overrides the config preservePanel option
50908      * @return {Roo.ContentPanel} The panel that was removed
50909      */
50910     remove : function(panel, preservePanel){
50911         panel = this.getPanel(panel);
50912         if(!panel){
50913             return null;
50914         }
50915         var e = {};
50916         this.fireEvent("beforeremove", this, panel, e);
50917         if(e.cancel === true){
50918             return null;
50919         }
50920         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50921         var panelId = panel.getId();
50922         this.panels.removeKey(panelId);
50923         if(preservePanel){
50924             document.body.appendChild(panel.getEl().dom);
50925         }
50926         if(this.tabs){
50927             this.tabs.removeTab(panel.getEl().id);
50928         }else if (!preservePanel){
50929             this.bodyEl.dom.removeChild(panel.getEl().dom);
50930         }
50931         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50932             var p = this.panels.first();
50933             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50934             tempEl.appendChild(p.getEl().dom);
50935             this.bodyEl.update("");
50936             this.bodyEl.dom.appendChild(p.getEl().dom);
50937             tempEl = null;
50938             this.updateTitle(p.getTitle());
50939             this.tabs = null;
50940             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50941             this.setActivePanel(p);
50942         }
50943         panel.setRegion(null);
50944         if(this.activePanel == panel){
50945             this.activePanel = null;
50946         }
50947         if(this.config.autoDestroy !== false && preservePanel !== true){
50948             try{panel.destroy();}catch(e){}
50949         }
50950         this.fireEvent("panelremoved", this, panel);
50951         return panel;
50952     },
50953
50954     /**
50955      * Returns the TabPanel component used by this region
50956      * @return {Roo.TabPanel}
50957      */
50958     getTabs : function(){
50959         return this.tabs;
50960     },
50961
50962     createTool : function(parentEl, className){
50963         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50964             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50965         btn.addClassOnOver("x-layout-tools-button-over");
50966         return btn;
50967     }
50968 });/*
50969  * Based on:
50970  * Ext JS Library 1.1.1
50971  * Copyright(c) 2006-2007, Ext JS, LLC.
50972  *
50973  * Originally Released Under LGPL - original licence link has changed is not relivant.
50974  *
50975  * Fork - LGPL
50976  * <script type="text/javascript">
50977  */
50978  
50979
50980
50981 /**
50982  * @class Roo.SplitLayoutRegion
50983  * @extends Roo.LayoutRegion
50984  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50985  */
50986 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50987     this.cursor = cursor;
50988     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50989 };
50990
50991 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50992     splitTip : "Drag to resize.",
50993     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50994     useSplitTips : false,
50995
50996     applyConfig : function(config){
50997         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50998         if(config.split){
50999             if(!this.split){
51000                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
51001                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
51002                 /** The SplitBar for this region 
51003                 * @type Roo.SplitBar */
51004                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
51005                 this.split.on("moved", this.onSplitMove, this);
51006                 this.split.useShim = config.useShim === true;
51007                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
51008                 if(this.useSplitTips){
51009                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
51010                 }
51011                 if(config.collapsible){
51012                     this.split.el.on("dblclick", this.collapse,  this);
51013                 }
51014             }
51015             if(typeof config.minSize != "undefined"){
51016                 this.split.minSize = config.minSize;
51017             }
51018             if(typeof config.maxSize != "undefined"){
51019                 this.split.maxSize = config.maxSize;
51020             }
51021             if(config.hideWhenEmpty || config.hidden || config.collapsed){
51022                 this.hideSplitter();
51023             }
51024         }
51025     },
51026
51027     getHMaxSize : function(){
51028          var cmax = this.config.maxSize || 10000;
51029          var center = this.mgr.getRegion("center");
51030          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
51031     },
51032
51033     getVMaxSize : function(){
51034          var cmax = this.config.maxSize || 10000;
51035          var center = this.mgr.getRegion("center");
51036          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
51037     },
51038
51039     onSplitMove : function(split, newSize){
51040         this.fireEvent("resized", this, newSize);
51041     },
51042     
51043     /** 
51044      * Returns the {@link Roo.SplitBar} for this region.
51045      * @return {Roo.SplitBar}
51046      */
51047     getSplitBar : function(){
51048         return this.split;
51049     },
51050     
51051     hide : function(){
51052         this.hideSplitter();
51053         Roo.SplitLayoutRegion.superclass.hide.call(this);
51054     },
51055
51056     hideSplitter : function(){
51057         if(this.split){
51058             this.split.el.setLocation(-2000,-2000);
51059             this.split.el.hide();
51060         }
51061     },
51062
51063     show : function(){
51064         if(this.split){
51065             this.split.el.show();
51066         }
51067         Roo.SplitLayoutRegion.superclass.show.call(this);
51068     },
51069     
51070     beforeSlide: function(){
51071         if(Roo.isGecko){// firefox overflow auto bug workaround
51072             this.bodyEl.clip();
51073             if(this.tabs) {
51074                 this.tabs.bodyEl.clip();
51075             }
51076             if(this.activePanel){
51077                 this.activePanel.getEl().clip();
51078                 
51079                 if(this.activePanel.beforeSlide){
51080                     this.activePanel.beforeSlide();
51081                 }
51082             }
51083         }
51084     },
51085     
51086     afterSlide : function(){
51087         if(Roo.isGecko){// firefox overflow auto bug workaround
51088             this.bodyEl.unclip();
51089             if(this.tabs) {
51090                 this.tabs.bodyEl.unclip();
51091             }
51092             if(this.activePanel){
51093                 this.activePanel.getEl().unclip();
51094                 if(this.activePanel.afterSlide){
51095                     this.activePanel.afterSlide();
51096                 }
51097             }
51098         }
51099     },
51100
51101     initAutoHide : function(){
51102         if(this.autoHide !== false){
51103             if(!this.autoHideHd){
51104                 var st = new Roo.util.DelayedTask(this.slideIn, this);
51105                 this.autoHideHd = {
51106                     "mouseout": function(e){
51107                         if(!e.within(this.el, true)){
51108                             st.delay(500);
51109                         }
51110                     },
51111                     "mouseover" : function(e){
51112                         st.cancel();
51113                     },
51114                     scope : this
51115                 };
51116             }
51117             this.el.on(this.autoHideHd);
51118         }
51119     },
51120
51121     clearAutoHide : function(){
51122         if(this.autoHide !== false){
51123             this.el.un("mouseout", this.autoHideHd.mouseout);
51124             this.el.un("mouseover", this.autoHideHd.mouseover);
51125         }
51126     },
51127
51128     clearMonitor : function(){
51129         Roo.get(document).un("click", this.slideInIf, this);
51130     },
51131
51132     // these names are backwards but not changed for compat
51133     slideOut : function(){
51134         if(this.isSlid || this.el.hasActiveFx()){
51135             return;
51136         }
51137         this.isSlid = true;
51138         if(this.collapseBtn){
51139             this.collapseBtn.hide();
51140         }
51141         this.closeBtnState = this.closeBtn.getStyle('display');
51142         this.closeBtn.hide();
51143         if(this.stickBtn){
51144             this.stickBtn.show();
51145         }
51146         this.el.show();
51147         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
51148         this.beforeSlide();
51149         this.el.setStyle("z-index", 10001);
51150         this.el.slideIn(this.getSlideAnchor(), {
51151             callback: function(){
51152                 this.afterSlide();
51153                 this.initAutoHide();
51154                 Roo.get(document).on("click", this.slideInIf, this);
51155                 this.fireEvent("slideshow", this);
51156             },
51157             scope: this,
51158             block: true
51159         });
51160     },
51161
51162     afterSlideIn : function(){
51163         this.clearAutoHide();
51164         this.isSlid = false;
51165         this.clearMonitor();
51166         this.el.setStyle("z-index", "");
51167         if(this.collapseBtn){
51168             this.collapseBtn.show();
51169         }
51170         this.closeBtn.setStyle('display', this.closeBtnState);
51171         if(this.stickBtn){
51172             this.stickBtn.hide();
51173         }
51174         this.fireEvent("slidehide", this);
51175     },
51176
51177     slideIn : function(cb){
51178         if(!this.isSlid || this.el.hasActiveFx()){
51179             Roo.callback(cb);
51180             return;
51181         }
51182         this.isSlid = false;
51183         this.beforeSlide();
51184         this.el.slideOut(this.getSlideAnchor(), {
51185             callback: function(){
51186                 this.el.setLeftTop(-10000, -10000);
51187                 this.afterSlide();
51188                 this.afterSlideIn();
51189                 Roo.callback(cb);
51190             },
51191             scope: this,
51192             block: true
51193         });
51194     },
51195     
51196     slideInIf : function(e){
51197         if(!e.within(this.el)){
51198             this.slideIn();
51199         }
51200     },
51201
51202     animateCollapse : function(){
51203         this.beforeSlide();
51204         this.el.setStyle("z-index", 20000);
51205         var anchor = this.getSlideAnchor();
51206         this.el.slideOut(anchor, {
51207             callback : function(){
51208                 this.el.setStyle("z-index", "");
51209                 this.collapsedEl.slideIn(anchor, {duration:.3});
51210                 this.afterSlide();
51211                 this.el.setLocation(-10000,-10000);
51212                 this.el.hide();
51213                 this.fireEvent("collapsed", this);
51214             },
51215             scope: this,
51216             block: true
51217         });
51218     },
51219
51220     animateExpand : function(){
51221         this.beforeSlide();
51222         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51223         this.el.setStyle("z-index", 20000);
51224         this.collapsedEl.hide({
51225             duration:.1
51226         });
51227         this.el.slideIn(this.getSlideAnchor(), {
51228             callback : function(){
51229                 this.el.setStyle("z-index", "");
51230                 this.afterSlide();
51231                 if(this.split){
51232                     this.split.el.show();
51233                 }
51234                 this.fireEvent("invalidated", this);
51235                 this.fireEvent("expanded", this);
51236             },
51237             scope: this,
51238             block: true
51239         });
51240     },
51241
51242     anchors : {
51243         "west" : "left",
51244         "east" : "right",
51245         "north" : "top",
51246         "south" : "bottom"
51247     },
51248
51249     sanchors : {
51250         "west" : "l",
51251         "east" : "r",
51252         "north" : "t",
51253         "south" : "b"
51254     },
51255
51256     canchors : {
51257         "west" : "tl-tr",
51258         "east" : "tr-tl",
51259         "north" : "tl-bl",
51260         "south" : "bl-tl"
51261     },
51262
51263     getAnchor : function(){
51264         return this.anchors[this.position];
51265     },
51266
51267     getCollapseAnchor : function(){
51268         return this.canchors[this.position];
51269     },
51270
51271     getSlideAnchor : function(){
51272         return this.sanchors[this.position];
51273     },
51274
51275     getAlignAdj : function(){
51276         var cm = this.cmargins;
51277         switch(this.position){
51278             case "west":
51279                 return [0, 0];
51280             break;
51281             case "east":
51282                 return [0, 0];
51283             break;
51284             case "north":
51285                 return [0, 0];
51286             break;
51287             case "south":
51288                 return [0, 0];
51289             break;
51290         }
51291     },
51292
51293     getExpandAdj : function(){
51294         var c = this.collapsedEl, cm = this.cmargins;
51295         switch(this.position){
51296             case "west":
51297                 return [-(cm.right+c.getWidth()+cm.left), 0];
51298             break;
51299             case "east":
51300                 return [cm.right+c.getWidth()+cm.left, 0];
51301             break;
51302             case "north":
51303                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51304             break;
51305             case "south":
51306                 return [0, cm.top+cm.bottom+c.getHeight()];
51307             break;
51308         }
51309     }
51310 });/*
51311  * Based on:
51312  * Ext JS Library 1.1.1
51313  * Copyright(c) 2006-2007, Ext JS, LLC.
51314  *
51315  * Originally Released Under LGPL - original licence link has changed is not relivant.
51316  *
51317  * Fork - LGPL
51318  * <script type="text/javascript">
51319  */
51320 /*
51321  * These classes are private internal classes
51322  */
51323 Roo.CenterLayoutRegion = function(mgr, config){
51324     Roo.LayoutRegion.call(this, mgr, config, "center");
51325     this.visible = true;
51326     this.minWidth = config.minWidth || 20;
51327     this.minHeight = config.minHeight || 20;
51328 };
51329
51330 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51331     hide : function(){
51332         // center panel can't be hidden
51333     },
51334     
51335     show : function(){
51336         // center panel can't be hidden
51337     },
51338     
51339     getMinWidth: function(){
51340         return this.minWidth;
51341     },
51342     
51343     getMinHeight: function(){
51344         return this.minHeight;
51345     }
51346 });
51347
51348
51349 Roo.NorthLayoutRegion = function(mgr, config){
51350     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51351     if(this.split){
51352         this.split.placement = Roo.SplitBar.TOP;
51353         this.split.orientation = Roo.SplitBar.VERTICAL;
51354         this.split.el.addClass("x-layout-split-v");
51355     }
51356     var size = config.initialSize || config.height;
51357     if(typeof size != "undefined"){
51358         this.el.setHeight(size);
51359     }
51360 };
51361 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51362     orientation: Roo.SplitBar.VERTICAL,
51363     getBox : function(){
51364         if(this.collapsed){
51365             return this.collapsedEl.getBox();
51366         }
51367         var box = this.el.getBox();
51368         if(this.split){
51369             box.height += this.split.el.getHeight();
51370         }
51371         return box;
51372     },
51373     
51374     updateBox : function(box){
51375         if(this.split && !this.collapsed){
51376             box.height -= this.split.el.getHeight();
51377             this.split.el.setLeft(box.x);
51378             this.split.el.setTop(box.y+box.height);
51379             this.split.el.setWidth(box.width);
51380         }
51381         if(this.collapsed){
51382             this.updateBody(box.width, null);
51383         }
51384         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51385     }
51386 });
51387
51388 Roo.SouthLayoutRegion = function(mgr, config){
51389     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51390     if(this.split){
51391         this.split.placement = Roo.SplitBar.BOTTOM;
51392         this.split.orientation = Roo.SplitBar.VERTICAL;
51393         this.split.el.addClass("x-layout-split-v");
51394     }
51395     var size = config.initialSize || config.height;
51396     if(typeof size != "undefined"){
51397         this.el.setHeight(size);
51398     }
51399 };
51400 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51401     orientation: Roo.SplitBar.VERTICAL,
51402     getBox : function(){
51403         if(this.collapsed){
51404             return this.collapsedEl.getBox();
51405         }
51406         var box = this.el.getBox();
51407         if(this.split){
51408             var sh = this.split.el.getHeight();
51409             box.height += sh;
51410             box.y -= sh;
51411         }
51412         return box;
51413     },
51414     
51415     updateBox : function(box){
51416         if(this.split && !this.collapsed){
51417             var sh = this.split.el.getHeight();
51418             box.height -= sh;
51419             box.y += sh;
51420             this.split.el.setLeft(box.x);
51421             this.split.el.setTop(box.y-sh);
51422             this.split.el.setWidth(box.width);
51423         }
51424         if(this.collapsed){
51425             this.updateBody(box.width, null);
51426         }
51427         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51428     }
51429 });
51430
51431 Roo.EastLayoutRegion = function(mgr, config){
51432     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51433     if(this.split){
51434         this.split.placement = Roo.SplitBar.RIGHT;
51435         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51436         this.split.el.addClass("x-layout-split-h");
51437     }
51438     var size = config.initialSize || config.width;
51439     if(typeof size != "undefined"){
51440         this.el.setWidth(size);
51441     }
51442 };
51443 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51444     orientation: Roo.SplitBar.HORIZONTAL,
51445     getBox : function(){
51446         if(this.collapsed){
51447             return this.collapsedEl.getBox();
51448         }
51449         var box = this.el.getBox();
51450         if(this.split){
51451             var sw = this.split.el.getWidth();
51452             box.width += sw;
51453             box.x -= sw;
51454         }
51455         return box;
51456     },
51457
51458     updateBox : function(box){
51459         if(this.split && !this.collapsed){
51460             var sw = this.split.el.getWidth();
51461             box.width -= sw;
51462             this.split.el.setLeft(box.x);
51463             this.split.el.setTop(box.y);
51464             this.split.el.setHeight(box.height);
51465             box.x += sw;
51466         }
51467         if(this.collapsed){
51468             this.updateBody(null, box.height);
51469         }
51470         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51471     }
51472 });
51473
51474 Roo.WestLayoutRegion = function(mgr, config){
51475     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51476     if(this.split){
51477         this.split.placement = Roo.SplitBar.LEFT;
51478         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51479         this.split.el.addClass("x-layout-split-h");
51480     }
51481     var size = config.initialSize || config.width;
51482     if(typeof size != "undefined"){
51483         this.el.setWidth(size);
51484     }
51485 };
51486 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51487     orientation: Roo.SplitBar.HORIZONTAL,
51488     getBox : function(){
51489         if(this.collapsed){
51490             return this.collapsedEl.getBox();
51491         }
51492         var box = this.el.getBox();
51493         if(this.split){
51494             box.width += this.split.el.getWidth();
51495         }
51496         return box;
51497     },
51498     
51499     updateBox : function(box){
51500         if(this.split && !this.collapsed){
51501             var sw = this.split.el.getWidth();
51502             box.width -= sw;
51503             this.split.el.setLeft(box.x+box.width);
51504             this.split.el.setTop(box.y);
51505             this.split.el.setHeight(box.height);
51506         }
51507         if(this.collapsed){
51508             this.updateBody(null, box.height);
51509         }
51510         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51511     }
51512 });
51513 /*
51514  * Based on:
51515  * Ext JS Library 1.1.1
51516  * Copyright(c) 2006-2007, Ext JS, LLC.
51517  *
51518  * Originally Released Under LGPL - original licence link has changed is not relivant.
51519  *
51520  * Fork - LGPL
51521  * <script type="text/javascript">
51522  */
51523  
51524  
51525 /*
51526  * Private internal class for reading and applying state
51527  */
51528 Roo.LayoutStateManager = function(layout){
51529      // default empty state
51530      this.state = {
51531         north: {},
51532         south: {},
51533         east: {},
51534         west: {}       
51535     };
51536 };
51537
51538 Roo.LayoutStateManager.prototype = {
51539     init : function(layout, provider){
51540         this.provider = provider;
51541         var state = provider.get(layout.id+"-layout-state");
51542         if(state){
51543             var wasUpdating = layout.isUpdating();
51544             if(!wasUpdating){
51545                 layout.beginUpdate();
51546             }
51547             for(var key in state){
51548                 if(typeof state[key] != "function"){
51549                     var rstate = state[key];
51550                     var r = layout.getRegion(key);
51551                     if(r && rstate){
51552                         if(rstate.size){
51553                             r.resizeTo(rstate.size);
51554                         }
51555                         if(rstate.collapsed == true){
51556                             r.collapse(true);
51557                         }else{
51558                             r.expand(null, true);
51559                         }
51560                     }
51561                 }
51562             }
51563             if(!wasUpdating){
51564                 layout.endUpdate();
51565             }
51566             this.state = state; 
51567         }
51568         this.layout = layout;
51569         layout.on("regionresized", this.onRegionResized, this);
51570         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51571         layout.on("regionexpanded", this.onRegionExpanded, this);
51572     },
51573     
51574     storeState : function(){
51575         this.provider.set(this.layout.id+"-layout-state", this.state);
51576     },
51577     
51578     onRegionResized : function(region, newSize){
51579         this.state[region.getPosition()].size = newSize;
51580         this.storeState();
51581     },
51582     
51583     onRegionCollapsed : function(region){
51584         this.state[region.getPosition()].collapsed = true;
51585         this.storeState();
51586     },
51587     
51588     onRegionExpanded : function(region){
51589         this.state[region.getPosition()].collapsed = false;
51590         this.storeState();
51591     }
51592 };/*
51593  * Based on:
51594  * Ext JS Library 1.1.1
51595  * Copyright(c) 2006-2007, Ext JS, LLC.
51596  *
51597  * Originally Released Under LGPL - original licence link has changed is not relivant.
51598  *
51599  * Fork - LGPL
51600  * <script type="text/javascript">
51601  */
51602 /**
51603  * @class Roo.ContentPanel
51604  * @extends Roo.util.Observable
51605  * A basic ContentPanel element.
51606  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51607  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51608  * @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
51609  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51610  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51611  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51612  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51613  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51614  * @cfg {String} title          The title for this panel
51615  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51616  * @cfg {String} url            Calls {@link #setUrl} with this value
51617  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51618  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51619  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51620  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51621
51622  * @constructor
51623  * Create a new ContentPanel.
51624  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51625  * @param {String/Object} config A string to set only the title or a config object
51626  * @param {String} content (optional) Set the HTML content for this panel
51627  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51628  */
51629 Roo.ContentPanel = function(el, config, content){
51630     
51631      
51632     /*
51633     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51634         config = el;
51635         el = Roo.id();
51636     }
51637     if (config && config.parentLayout) { 
51638         el = config.parentLayout.el.createChild(); 
51639     }
51640     */
51641     if(el.autoCreate){ // xtype is available if this is called from factory
51642         config = el;
51643         el = Roo.id();
51644     }
51645     this.el = Roo.get(el);
51646     if(!this.el && config && config.autoCreate){
51647         if(typeof config.autoCreate == "object"){
51648             if(!config.autoCreate.id){
51649                 config.autoCreate.id = config.id||el;
51650             }
51651             this.el = Roo.DomHelper.append(document.body,
51652                         config.autoCreate, true);
51653         }else{
51654             this.el = Roo.DomHelper.append(document.body,
51655                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51656         }
51657     }
51658     this.closable = false;
51659     this.loaded = false;
51660     this.active = false;
51661     if(typeof config == "string"){
51662         this.title = config;
51663     }else{
51664         Roo.apply(this, config);
51665     }
51666     
51667     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51668         this.wrapEl = this.el.wrap();
51669         this.toolbar.container = this.el.insertSibling(false, 'before');
51670         this.toolbar = new Roo.Toolbar(this.toolbar);
51671     }
51672     
51673     // xtype created footer. - not sure if will work as we normally have to render first..
51674     if (this.footer && !this.footer.el && this.footer.xtype) {
51675         if (!this.wrapEl) {
51676             this.wrapEl = this.el.wrap();
51677         }
51678     
51679         this.footer.container = this.wrapEl.createChild();
51680          
51681         this.footer = Roo.factory(this.footer, Roo);
51682         
51683     }
51684     
51685     if(this.resizeEl){
51686         this.resizeEl = Roo.get(this.resizeEl, true);
51687     }else{
51688         this.resizeEl = this.el;
51689     }
51690     // handle view.xtype
51691     
51692  
51693     
51694     
51695     this.addEvents({
51696         /**
51697          * @event activate
51698          * Fires when this panel is activated. 
51699          * @param {Roo.ContentPanel} this
51700          */
51701         "activate" : true,
51702         /**
51703          * @event deactivate
51704          * Fires when this panel is activated. 
51705          * @param {Roo.ContentPanel} this
51706          */
51707         "deactivate" : true,
51708
51709         /**
51710          * @event resize
51711          * Fires when this panel is resized if fitToFrame is true.
51712          * @param {Roo.ContentPanel} this
51713          * @param {Number} width The width after any component adjustments
51714          * @param {Number} height The height after any component adjustments
51715          */
51716         "resize" : true,
51717         
51718          /**
51719          * @event render
51720          * Fires when this tab is created
51721          * @param {Roo.ContentPanel} this
51722          */
51723         "render" : true
51724         
51725         
51726         
51727     });
51728     
51729
51730     
51731     
51732     if(this.autoScroll){
51733         this.resizeEl.setStyle("overflow", "auto");
51734     } else {
51735         // fix randome scrolling
51736         this.el.on('scroll', function() {
51737             Roo.log('fix random scolling');
51738             this.scrollTo('top',0); 
51739         });
51740     }
51741     content = content || this.content;
51742     if(content){
51743         this.setContent(content);
51744     }
51745     if(config && config.url){
51746         this.setUrl(this.url, this.params, this.loadOnce);
51747     }
51748     
51749     
51750     
51751     Roo.ContentPanel.superclass.constructor.call(this);
51752     
51753     if (this.view && typeof(this.view.xtype) != 'undefined') {
51754         this.view.el = this.el.appendChild(document.createElement("div"));
51755         this.view = Roo.factory(this.view); 
51756         this.view.render  &&  this.view.render(false, '');  
51757     }
51758     
51759     
51760     this.fireEvent('render', this);
51761 };
51762
51763 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51764     tabTip:'',
51765     setRegion : function(region){
51766         this.region = region;
51767         if(region){
51768            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51769         }else{
51770            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51771         } 
51772     },
51773     
51774     /**
51775      * Returns the toolbar for this Panel if one was configured. 
51776      * @return {Roo.Toolbar} 
51777      */
51778     getToolbar : function(){
51779         return this.toolbar;
51780     },
51781     
51782     setActiveState : function(active){
51783         this.active = active;
51784         if(!active){
51785             this.fireEvent("deactivate", this);
51786         }else{
51787             this.fireEvent("activate", this);
51788         }
51789     },
51790     /**
51791      * Updates this panel's element
51792      * @param {String} content The new content
51793      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51794     */
51795     setContent : function(content, loadScripts){
51796         this.el.update(content, loadScripts);
51797     },
51798
51799     ignoreResize : function(w, h){
51800         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51801             return true;
51802         }else{
51803             this.lastSize = {width: w, height: h};
51804             return false;
51805         }
51806     },
51807     /**
51808      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51809      * @return {Roo.UpdateManager} The UpdateManager
51810      */
51811     getUpdateManager : function(){
51812         return this.el.getUpdateManager();
51813     },
51814      /**
51815      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51816      * @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:
51817 <pre><code>
51818 panel.load({
51819     url: "your-url.php",
51820     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51821     callback: yourFunction,
51822     scope: yourObject, //(optional scope)
51823     discardUrl: false,
51824     nocache: false,
51825     text: "Loading...",
51826     timeout: 30,
51827     scripts: false
51828 });
51829 </code></pre>
51830      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51831      * 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.
51832      * @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}
51833      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51834      * @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.
51835      * @return {Roo.ContentPanel} this
51836      */
51837     load : function(){
51838         var um = this.el.getUpdateManager();
51839         um.update.apply(um, arguments);
51840         return this;
51841     },
51842
51843
51844     /**
51845      * 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.
51846      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51847      * @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)
51848      * @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)
51849      * @return {Roo.UpdateManager} The UpdateManager
51850      */
51851     setUrl : function(url, params, loadOnce){
51852         if(this.refreshDelegate){
51853             this.removeListener("activate", this.refreshDelegate);
51854         }
51855         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51856         this.on("activate", this.refreshDelegate);
51857         return this.el.getUpdateManager();
51858     },
51859     
51860     _handleRefresh : function(url, params, loadOnce){
51861         if(!loadOnce || !this.loaded){
51862             var updater = this.el.getUpdateManager();
51863             updater.update(url, params, this._setLoaded.createDelegate(this));
51864         }
51865     },
51866     
51867     _setLoaded : function(){
51868         this.loaded = true;
51869     }, 
51870     
51871     /**
51872      * Returns this panel's id
51873      * @return {String} 
51874      */
51875     getId : function(){
51876         return this.el.id;
51877     },
51878     
51879     /** 
51880      * Returns this panel's element - used by regiosn to add.
51881      * @return {Roo.Element} 
51882      */
51883     getEl : function(){
51884         return this.wrapEl || this.el;
51885     },
51886     
51887     adjustForComponents : function(width, height)
51888     {
51889         //Roo.log('adjustForComponents ');
51890         if(this.resizeEl != this.el){
51891             width -= this.el.getFrameWidth('lr');
51892             height -= this.el.getFrameWidth('tb');
51893         }
51894         if(this.toolbar){
51895             var te = this.toolbar.getEl();
51896             height -= te.getHeight();
51897             te.setWidth(width);
51898         }
51899         if(this.footer){
51900             var te = this.footer.getEl();
51901             Roo.log("footer:" + te.getHeight());
51902             
51903             height -= te.getHeight();
51904             te.setWidth(width);
51905         }
51906         
51907         
51908         if(this.adjustments){
51909             width += this.adjustments[0];
51910             height += this.adjustments[1];
51911         }
51912         return {"width": width, "height": height};
51913     },
51914     
51915     setSize : function(width, height){
51916         if(this.fitToFrame && !this.ignoreResize(width, height)){
51917             if(this.fitContainer && this.resizeEl != this.el){
51918                 this.el.setSize(width, height);
51919             }
51920             var size = this.adjustForComponents(width, height);
51921             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51922             this.fireEvent('resize', this, size.width, size.height);
51923         }
51924     },
51925     
51926     /**
51927      * Returns this panel's title
51928      * @return {String} 
51929      */
51930     getTitle : function(){
51931         return this.title;
51932     },
51933     
51934     /**
51935      * Set this panel's title
51936      * @param {String} title
51937      */
51938     setTitle : function(title){
51939         this.title = title;
51940         if(this.region){
51941             this.region.updatePanelTitle(this, title);
51942         }
51943     },
51944     
51945     /**
51946      * Returns true is this panel was configured to be closable
51947      * @return {Boolean} 
51948      */
51949     isClosable : function(){
51950         return this.closable;
51951     },
51952     
51953     beforeSlide : function(){
51954         this.el.clip();
51955         this.resizeEl.clip();
51956     },
51957     
51958     afterSlide : function(){
51959         this.el.unclip();
51960         this.resizeEl.unclip();
51961     },
51962     
51963     /**
51964      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51965      *   Will fail silently if the {@link #setUrl} method has not been called.
51966      *   This does not activate the panel, just updates its content.
51967      */
51968     refresh : function(){
51969         if(this.refreshDelegate){
51970            this.loaded = false;
51971            this.refreshDelegate();
51972         }
51973     },
51974     
51975     /**
51976      * Destroys this panel
51977      */
51978     destroy : function(){
51979         this.el.removeAllListeners();
51980         var tempEl = document.createElement("span");
51981         tempEl.appendChild(this.el.dom);
51982         tempEl.innerHTML = "";
51983         this.el.remove();
51984         this.el = null;
51985     },
51986     
51987     /**
51988      * form - if the content panel contains a form - this is a reference to it.
51989      * @type {Roo.form.Form}
51990      */
51991     form : false,
51992     /**
51993      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51994      *    This contains a reference to it.
51995      * @type {Roo.View}
51996      */
51997     view : false,
51998     
51999       /**
52000      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
52001      * <pre><code>
52002
52003 layout.addxtype({
52004        xtype : 'Form',
52005        items: [ .... ]
52006    }
52007 );
52008
52009 </code></pre>
52010      * @param {Object} cfg Xtype definition of item to add.
52011      */
52012     
52013     addxtype : function(cfg) {
52014         // add form..
52015         if (cfg.xtype.match(/^Form$/)) {
52016             
52017             var el;
52018             //if (this.footer) {
52019             //    el = this.footer.container.insertSibling(false, 'before');
52020             //} else {
52021                 el = this.el.createChild();
52022             //}
52023
52024             this.form = new  Roo.form.Form(cfg);
52025             
52026             
52027             if ( this.form.allItems.length) {
52028                 this.form.render(el.dom);
52029             }
52030             return this.form;
52031         }
52032         // should only have one of theses..
52033         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
52034             // views.. should not be just added - used named prop 'view''
52035             
52036             cfg.el = this.el.appendChild(document.createElement("div"));
52037             // factory?
52038             
52039             var ret = new Roo.factory(cfg);
52040              
52041              ret.render && ret.render(false, ''); // render blank..
52042             this.view = ret;
52043             return ret;
52044         }
52045         return false;
52046     }
52047 });
52048
52049 /**
52050  * @class Roo.GridPanel
52051  * @extends Roo.ContentPanel
52052  * @constructor
52053  * Create a new GridPanel.
52054  * @param {Roo.grid.Grid} grid The grid for this panel
52055  * @param {String/Object} config A string to set only the panel's title, or a config object
52056  */
52057 Roo.GridPanel = function(grid, config){
52058     
52059   
52060     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
52061         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
52062         
52063     this.wrapper.dom.appendChild(grid.getGridEl().dom);
52064     
52065     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
52066     
52067     if(this.toolbar){
52068         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
52069     }
52070     // xtype created footer. - not sure if will work as we normally have to render first..
52071     if (this.footer && !this.footer.el && this.footer.xtype) {
52072         
52073         this.footer.container = this.grid.getView().getFooterPanel(true);
52074         this.footer.dataSource = this.grid.dataSource;
52075         this.footer = Roo.factory(this.footer, Roo);
52076         
52077     }
52078     
52079     grid.monitorWindowResize = false; // turn off autosizing
52080     grid.autoHeight = false;
52081     grid.autoWidth = false;
52082     this.grid = grid;
52083     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
52084 };
52085
52086 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
52087     getId : function(){
52088         return this.grid.id;
52089     },
52090     
52091     /**
52092      * Returns the grid for this panel
52093      * @return {Roo.grid.Grid} 
52094      */
52095     getGrid : function(){
52096         return this.grid;    
52097     },
52098     
52099     setSize : function(width, height){
52100         if(!this.ignoreResize(width, height)){
52101             var grid = this.grid;
52102             var size = this.adjustForComponents(width, height);
52103             grid.getGridEl().setSize(size.width, size.height);
52104             grid.autoSize();
52105         }
52106     },
52107     
52108     beforeSlide : function(){
52109         this.grid.getView().scroller.clip();
52110     },
52111     
52112     afterSlide : function(){
52113         this.grid.getView().scroller.unclip();
52114     },
52115     
52116     destroy : function(){
52117         this.grid.destroy();
52118         delete this.grid;
52119         Roo.GridPanel.superclass.destroy.call(this); 
52120     }
52121 });
52122
52123
52124 /**
52125  * @class Roo.NestedLayoutPanel
52126  * @extends Roo.ContentPanel
52127  * @constructor
52128  * Create a new NestedLayoutPanel.
52129  * 
52130  * 
52131  * @param {Roo.BorderLayout} layout The layout for this panel
52132  * @param {String/Object} config A string to set only the title or a config object
52133  */
52134 Roo.NestedLayoutPanel = function(layout, config)
52135 {
52136     // construct with only one argument..
52137     /* FIXME - implement nicer consturctors
52138     if (layout.layout) {
52139         config = layout;
52140         layout = config.layout;
52141         delete config.layout;
52142     }
52143     if (layout.xtype && !layout.getEl) {
52144         // then layout needs constructing..
52145         layout = Roo.factory(layout, Roo);
52146     }
52147     */
52148     
52149     
52150     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
52151     
52152     layout.monitorWindowResize = false; // turn off autosizing
52153     this.layout = layout;
52154     this.layout.getEl().addClass("x-layout-nested-layout");
52155     
52156     
52157     
52158     
52159 };
52160
52161 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52162
52163     setSize : function(width, height){
52164         if(!this.ignoreResize(width, height)){
52165             var size = this.adjustForComponents(width, height);
52166             var el = this.layout.getEl();
52167             el.setSize(size.width, size.height);
52168             var touch = el.dom.offsetWidth;
52169             this.layout.layout();
52170             // ie requires a double layout on the first pass
52171             if(Roo.isIE && !this.initialized){
52172                 this.initialized = true;
52173                 this.layout.layout();
52174             }
52175         }
52176     },
52177     
52178     // activate all subpanels if not currently active..
52179     
52180     setActiveState : function(active){
52181         this.active = active;
52182         if(!active){
52183             this.fireEvent("deactivate", this);
52184             return;
52185         }
52186         
52187         this.fireEvent("activate", this);
52188         // not sure if this should happen before or after..
52189         if (!this.layout) {
52190             return; // should not happen..
52191         }
52192         var reg = false;
52193         for (var r in this.layout.regions) {
52194             reg = this.layout.getRegion(r);
52195             if (reg.getActivePanel()) {
52196                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52197                 reg.setActivePanel(reg.getActivePanel());
52198                 continue;
52199             }
52200             if (!reg.panels.length) {
52201                 continue;
52202             }
52203             reg.showPanel(reg.getPanel(0));
52204         }
52205         
52206         
52207         
52208         
52209     },
52210     
52211     /**
52212      * Returns the nested BorderLayout for this panel
52213      * @return {Roo.BorderLayout} 
52214      */
52215     getLayout : function(){
52216         return this.layout;
52217     },
52218     
52219      /**
52220      * Adds a xtype elements to the layout of the nested panel
52221      * <pre><code>
52222
52223 panel.addxtype({
52224        xtype : 'ContentPanel',
52225        region: 'west',
52226        items: [ .... ]
52227    }
52228 );
52229
52230 panel.addxtype({
52231         xtype : 'NestedLayoutPanel',
52232         region: 'west',
52233         layout: {
52234            center: { },
52235            west: { }   
52236         },
52237         items : [ ... list of content panels or nested layout panels.. ]
52238    }
52239 );
52240 </code></pre>
52241      * @param {Object} cfg Xtype definition of item to add.
52242      */
52243     addxtype : function(cfg) {
52244         return this.layout.addxtype(cfg);
52245     
52246     }
52247 });
52248
52249 Roo.ScrollPanel = function(el, config, content){
52250     config = config || {};
52251     config.fitToFrame = true;
52252     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52253     
52254     this.el.dom.style.overflow = "hidden";
52255     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52256     this.el.removeClass("x-layout-inactive-content");
52257     this.el.on("mousewheel", this.onWheel, this);
52258
52259     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52260     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52261     up.unselectable(); down.unselectable();
52262     up.on("click", this.scrollUp, this);
52263     down.on("click", this.scrollDown, this);
52264     up.addClassOnOver("x-scroller-btn-over");
52265     down.addClassOnOver("x-scroller-btn-over");
52266     up.addClassOnClick("x-scroller-btn-click");
52267     down.addClassOnClick("x-scroller-btn-click");
52268     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52269
52270     this.resizeEl = this.el;
52271     this.el = wrap; this.up = up; this.down = down;
52272 };
52273
52274 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52275     increment : 100,
52276     wheelIncrement : 5,
52277     scrollUp : function(){
52278         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52279     },
52280
52281     scrollDown : function(){
52282         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52283     },
52284
52285     afterScroll : function(){
52286         var el = this.resizeEl;
52287         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52288         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52289         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52290     },
52291
52292     setSize : function(){
52293         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52294         this.afterScroll();
52295     },
52296
52297     onWheel : function(e){
52298         var d = e.getWheelDelta();
52299         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52300         this.afterScroll();
52301         e.stopEvent();
52302     },
52303
52304     setContent : function(content, loadScripts){
52305         this.resizeEl.update(content, loadScripts);
52306     }
52307
52308 });
52309
52310
52311
52312
52313
52314
52315
52316
52317
52318 /**
52319  * @class Roo.TreePanel
52320  * @extends Roo.ContentPanel
52321  * @constructor
52322  * Create a new TreePanel. - defaults to fit/scoll contents.
52323  * @param {String/Object} config A string to set only the panel's title, or a config object
52324  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52325  */
52326 Roo.TreePanel = function(config){
52327     var el = config.el;
52328     var tree = config.tree;
52329     delete config.tree; 
52330     delete config.el; // hopefull!
52331     
52332     // wrapper for IE7 strict & safari scroll issue
52333     
52334     var treeEl = el.createChild();
52335     config.resizeEl = treeEl;
52336     
52337     
52338     
52339     Roo.TreePanel.superclass.constructor.call(this, el, config);
52340  
52341  
52342     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52343     //console.log(tree);
52344     this.on('activate', function()
52345     {
52346         if (this.tree.rendered) {
52347             return;
52348         }
52349         //console.log('render tree');
52350         this.tree.render();
52351     });
52352     // this should not be needed.. - it's actually the 'el' that resizes?
52353     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52354     
52355     //this.on('resize',  function (cp, w, h) {
52356     //        this.tree.innerCt.setWidth(w);
52357     //        this.tree.innerCt.setHeight(h);
52358     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52359     //});
52360
52361         
52362     
52363 };
52364
52365 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52366     fitToFrame : true,
52367     autoScroll : true
52368 });
52369
52370
52371
52372
52373
52374
52375
52376
52377
52378
52379
52380 /*
52381  * Based on:
52382  * Ext JS Library 1.1.1
52383  * Copyright(c) 2006-2007, Ext JS, LLC.
52384  *
52385  * Originally Released Under LGPL - original licence link has changed is not relivant.
52386  *
52387  * Fork - LGPL
52388  * <script type="text/javascript">
52389  */
52390  
52391
52392 /**
52393  * @class Roo.ReaderLayout
52394  * @extends Roo.BorderLayout
52395  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52396  * center region containing two nested regions (a top one for a list view and one for item preview below),
52397  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52398  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52399  * expedites the setup of the overall layout and regions for this common application style.
52400  * Example:
52401  <pre><code>
52402 var reader = new Roo.ReaderLayout();
52403 var CP = Roo.ContentPanel;  // shortcut for adding
52404
52405 reader.beginUpdate();
52406 reader.add("north", new CP("north", "North"));
52407 reader.add("west", new CP("west", {title: "West"}));
52408 reader.add("east", new CP("east", {title: "East"}));
52409
52410 reader.regions.listView.add(new CP("listView", "List"));
52411 reader.regions.preview.add(new CP("preview", "Preview"));
52412 reader.endUpdate();
52413 </code></pre>
52414 * @constructor
52415 * Create a new ReaderLayout
52416 * @param {Object} config Configuration options
52417 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52418 * document.body if omitted)
52419 */
52420 Roo.ReaderLayout = function(config, renderTo){
52421     var c = config || {size:{}};
52422     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52423         north: c.north !== false ? Roo.apply({
52424             split:false,
52425             initialSize: 32,
52426             titlebar: false
52427         }, c.north) : false,
52428         west: c.west !== false ? Roo.apply({
52429             split:true,
52430             initialSize: 200,
52431             minSize: 175,
52432             maxSize: 400,
52433             titlebar: true,
52434             collapsible: true,
52435             animate: true,
52436             margins:{left:5,right:0,bottom:5,top:5},
52437             cmargins:{left:5,right:5,bottom:5,top:5}
52438         }, c.west) : false,
52439         east: c.east !== false ? Roo.apply({
52440             split:true,
52441             initialSize: 200,
52442             minSize: 175,
52443             maxSize: 400,
52444             titlebar: true,
52445             collapsible: true,
52446             animate: true,
52447             margins:{left:0,right:5,bottom:5,top:5},
52448             cmargins:{left:5,right:5,bottom:5,top:5}
52449         }, c.east) : false,
52450         center: Roo.apply({
52451             tabPosition: 'top',
52452             autoScroll:false,
52453             closeOnTab: true,
52454             titlebar:false,
52455             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52456         }, c.center)
52457     });
52458
52459     this.el.addClass('x-reader');
52460
52461     this.beginUpdate();
52462
52463     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52464         south: c.preview !== false ? Roo.apply({
52465             split:true,
52466             initialSize: 200,
52467             minSize: 100,
52468             autoScroll:true,
52469             collapsible:true,
52470             titlebar: true,
52471             cmargins:{top:5,left:0, right:0, bottom:0}
52472         }, c.preview) : false,
52473         center: Roo.apply({
52474             autoScroll:false,
52475             titlebar:false,
52476             minHeight:200
52477         }, c.listView)
52478     });
52479     this.add('center', new Roo.NestedLayoutPanel(inner,
52480             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52481
52482     this.endUpdate();
52483
52484     this.regions.preview = inner.getRegion('south');
52485     this.regions.listView = inner.getRegion('center');
52486 };
52487
52488 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52489  * Based on:
52490  * Ext JS Library 1.1.1
52491  * Copyright(c) 2006-2007, Ext JS, LLC.
52492  *
52493  * Originally Released Under LGPL - original licence link has changed is not relivant.
52494  *
52495  * Fork - LGPL
52496  * <script type="text/javascript">
52497  */
52498  
52499 /**
52500  * @class Roo.grid.Grid
52501  * @extends Roo.util.Observable
52502  * This class represents the primary interface of a component based grid control.
52503  * <br><br>Usage:<pre><code>
52504  var grid = new Roo.grid.Grid("my-container-id", {
52505      ds: myDataStore,
52506      cm: myColModel,
52507      selModel: mySelectionModel,
52508      autoSizeColumns: true,
52509      monitorWindowResize: false,
52510      trackMouseOver: true
52511  });
52512  // set any options
52513  grid.render();
52514  * </code></pre>
52515  * <b>Common Problems:</b><br/>
52516  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52517  * element will correct this<br/>
52518  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52519  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52520  * are unpredictable.<br/>
52521  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52522  * grid to calculate dimensions/offsets.<br/>
52523   * @constructor
52524  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52525  * The container MUST have some type of size defined for the grid to fill. The container will be
52526  * automatically set to position relative if it isn't already.
52527  * @param {Object} config A config object that sets properties on this grid.
52528  */
52529 Roo.grid.Grid = function(container, config){
52530         // initialize the container
52531         this.container = Roo.get(container);
52532         this.container.update("");
52533         this.container.setStyle("overflow", "hidden");
52534     this.container.addClass('x-grid-container');
52535
52536     this.id = this.container.id;
52537
52538     Roo.apply(this, config);
52539     // check and correct shorthanded configs
52540     if(this.ds){
52541         this.dataSource = this.ds;
52542         delete this.ds;
52543     }
52544     if(this.cm){
52545         this.colModel = this.cm;
52546         delete this.cm;
52547     }
52548     if(this.sm){
52549         this.selModel = this.sm;
52550         delete this.sm;
52551     }
52552
52553     if (this.selModel) {
52554         this.selModel = Roo.factory(this.selModel, Roo.grid);
52555         this.sm = this.selModel;
52556         this.sm.xmodule = this.xmodule || false;
52557     }
52558     if (typeof(this.colModel.config) == 'undefined') {
52559         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52560         this.cm = this.colModel;
52561         this.cm.xmodule = this.xmodule || false;
52562     }
52563     if (this.dataSource) {
52564         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52565         this.ds = this.dataSource;
52566         this.ds.xmodule = this.xmodule || false;
52567          
52568     }
52569     
52570     
52571     
52572     if(this.width){
52573         this.container.setWidth(this.width);
52574     }
52575
52576     if(this.height){
52577         this.container.setHeight(this.height);
52578     }
52579     /** @private */
52580         this.addEvents({
52581         // raw events
52582         /**
52583          * @event click
52584          * The raw click event for the entire grid.
52585          * @param {Roo.EventObject} e
52586          */
52587         "click" : true,
52588         /**
52589          * @event dblclick
52590          * The raw dblclick event for the entire grid.
52591          * @param {Roo.EventObject} e
52592          */
52593         "dblclick" : true,
52594         /**
52595          * @event contextmenu
52596          * The raw contextmenu event for the entire grid.
52597          * @param {Roo.EventObject} e
52598          */
52599         "contextmenu" : true,
52600         /**
52601          * @event mousedown
52602          * The raw mousedown event for the entire grid.
52603          * @param {Roo.EventObject} e
52604          */
52605         "mousedown" : true,
52606         /**
52607          * @event mouseup
52608          * The raw mouseup event for the entire grid.
52609          * @param {Roo.EventObject} e
52610          */
52611         "mouseup" : true,
52612         /**
52613          * @event mouseover
52614          * The raw mouseover event for the entire grid.
52615          * @param {Roo.EventObject} e
52616          */
52617         "mouseover" : true,
52618         /**
52619          * @event mouseout
52620          * The raw mouseout event for the entire grid.
52621          * @param {Roo.EventObject} e
52622          */
52623         "mouseout" : true,
52624         /**
52625          * @event keypress
52626          * The raw keypress event for the entire grid.
52627          * @param {Roo.EventObject} e
52628          */
52629         "keypress" : true,
52630         /**
52631          * @event keydown
52632          * The raw keydown event for the entire grid.
52633          * @param {Roo.EventObject} e
52634          */
52635         "keydown" : true,
52636
52637         // custom events
52638
52639         /**
52640          * @event cellclick
52641          * Fires when a cell is clicked
52642          * @param {Grid} this
52643          * @param {Number} rowIndex
52644          * @param {Number} columnIndex
52645          * @param {Roo.EventObject} e
52646          */
52647         "cellclick" : true,
52648         /**
52649          * @event celldblclick
52650          * Fires when a cell is double clicked
52651          * @param {Grid} this
52652          * @param {Number} rowIndex
52653          * @param {Number} columnIndex
52654          * @param {Roo.EventObject} e
52655          */
52656         "celldblclick" : true,
52657         /**
52658          * @event rowclick
52659          * Fires when a row is clicked
52660          * @param {Grid} this
52661          * @param {Number} rowIndex
52662          * @param {Roo.EventObject} e
52663          */
52664         "rowclick" : true,
52665         /**
52666          * @event rowdblclick
52667          * Fires when a row is double clicked
52668          * @param {Grid} this
52669          * @param {Number} rowIndex
52670          * @param {Roo.EventObject} e
52671          */
52672         "rowdblclick" : true,
52673         /**
52674          * @event headerclick
52675          * Fires when a header is clicked
52676          * @param {Grid} this
52677          * @param {Number} columnIndex
52678          * @param {Roo.EventObject} e
52679          */
52680         "headerclick" : true,
52681         /**
52682          * @event headerdblclick
52683          * Fires when a header cell is double clicked
52684          * @param {Grid} this
52685          * @param {Number} columnIndex
52686          * @param {Roo.EventObject} e
52687          */
52688         "headerdblclick" : true,
52689         /**
52690          * @event rowcontextmenu
52691          * Fires when a row is right clicked
52692          * @param {Grid} this
52693          * @param {Number} rowIndex
52694          * @param {Roo.EventObject} e
52695          */
52696         "rowcontextmenu" : true,
52697         /**
52698          * @event cellcontextmenu
52699          * Fires when a cell is right clicked
52700          * @param {Grid} this
52701          * @param {Number} rowIndex
52702          * @param {Number} cellIndex
52703          * @param {Roo.EventObject} e
52704          */
52705          "cellcontextmenu" : true,
52706         /**
52707          * @event headercontextmenu
52708          * Fires when a header is right clicked
52709          * @param {Grid} this
52710          * @param {Number} columnIndex
52711          * @param {Roo.EventObject} e
52712          */
52713         "headercontextmenu" : true,
52714         /**
52715          * @event bodyscroll
52716          * Fires when the body element is scrolled
52717          * @param {Number} scrollLeft
52718          * @param {Number} scrollTop
52719          */
52720         "bodyscroll" : true,
52721         /**
52722          * @event columnresize
52723          * Fires when the user resizes a column
52724          * @param {Number} columnIndex
52725          * @param {Number} newSize
52726          */
52727         "columnresize" : true,
52728         /**
52729          * @event columnmove
52730          * Fires when the user moves a column
52731          * @param {Number} oldIndex
52732          * @param {Number} newIndex
52733          */
52734         "columnmove" : true,
52735         /**
52736          * @event startdrag
52737          * Fires when row(s) start being dragged
52738          * @param {Grid} this
52739          * @param {Roo.GridDD} dd The drag drop object
52740          * @param {event} e The raw browser event
52741          */
52742         "startdrag" : true,
52743         /**
52744          * @event enddrag
52745          * Fires when a drag operation is complete
52746          * @param {Grid} this
52747          * @param {Roo.GridDD} dd The drag drop object
52748          * @param {event} e The raw browser event
52749          */
52750         "enddrag" : true,
52751         /**
52752          * @event dragdrop
52753          * Fires when dragged row(s) are dropped on a valid DD target
52754          * @param {Grid} this
52755          * @param {Roo.GridDD} dd The drag drop object
52756          * @param {String} targetId The target drag drop object
52757          * @param {event} e The raw browser event
52758          */
52759         "dragdrop" : true,
52760         /**
52761          * @event dragover
52762          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52763          * @param {Grid} this
52764          * @param {Roo.GridDD} dd The drag drop object
52765          * @param {String} targetId The target drag drop object
52766          * @param {event} e The raw browser event
52767          */
52768         "dragover" : true,
52769         /**
52770          * @event dragenter
52771          *  Fires when the dragged row(s) first cross another DD target while being dragged
52772          * @param {Grid} this
52773          * @param {Roo.GridDD} dd The drag drop object
52774          * @param {String} targetId The target drag drop object
52775          * @param {event} e The raw browser event
52776          */
52777         "dragenter" : true,
52778         /**
52779          * @event dragout
52780          * Fires when the dragged row(s) leave another DD target while being dragged
52781          * @param {Grid} this
52782          * @param {Roo.GridDD} dd The drag drop object
52783          * @param {String} targetId The target drag drop object
52784          * @param {event} e The raw browser event
52785          */
52786         "dragout" : true,
52787         /**
52788          * @event rowclass
52789          * Fires when a row is rendered, so you can change add a style to it.
52790          * @param {GridView} gridview   The grid view
52791          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52792          */
52793         'rowclass' : true,
52794
52795         /**
52796          * @event render
52797          * Fires when the grid is rendered
52798          * @param {Grid} grid
52799          */
52800         'render' : true
52801     });
52802
52803     Roo.grid.Grid.superclass.constructor.call(this);
52804 };
52805 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52806     
52807     /**
52808      * @cfg {String} ddGroup - drag drop group.
52809      */
52810
52811     /**
52812      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52813      */
52814     minColumnWidth : 25,
52815
52816     /**
52817      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52818      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52819      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52820      */
52821     autoSizeColumns : false,
52822
52823     /**
52824      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52825      */
52826     autoSizeHeaders : true,
52827
52828     /**
52829      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52830      */
52831     monitorWindowResize : true,
52832
52833     /**
52834      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52835      * rows measured to get a columns size. Default is 0 (all rows).
52836      */
52837     maxRowsToMeasure : 0,
52838
52839     /**
52840      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52841      */
52842     trackMouseOver : true,
52843
52844     /**
52845     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52846     */
52847     
52848     /**
52849     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52850     */
52851     enableDragDrop : false,
52852     
52853     /**
52854     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52855     */
52856     enableColumnMove : true,
52857     
52858     /**
52859     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52860     */
52861     enableColumnHide : true,
52862     
52863     /**
52864     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52865     */
52866     enableRowHeightSync : false,
52867     
52868     /**
52869     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52870     */
52871     stripeRows : true,
52872     
52873     /**
52874     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52875     */
52876     autoHeight : false,
52877
52878     /**
52879      * @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.
52880      */
52881     autoExpandColumn : false,
52882
52883     /**
52884     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52885     * Default is 50.
52886     */
52887     autoExpandMin : 50,
52888
52889     /**
52890     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52891     */
52892     autoExpandMax : 1000,
52893
52894     /**
52895     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52896     */
52897     view : null,
52898
52899     /**
52900     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52901     */
52902     loadMask : false,
52903     /**
52904     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52905     */
52906     dropTarget: false,
52907     
52908    
52909     
52910     // private
52911     rendered : false,
52912
52913     /**
52914     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52915     * of a fixed width. Default is false.
52916     */
52917     /**
52918     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52919     */
52920     /**
52921      * Called once after all setup has been completed and the grid is ready to be rendered.
52922      * @return {Roo.grid.Grid} this
52923      */
52924     render : function()
52925     {
52926         var c = this.container;
52927         // try to detect autoHeight/width mode
52928         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52929             this.autoHeight = true;
52930         }
52931         var view = this.getView();
52932         view.init(this);
52933
52934         c.on("click", this.onClick, this);
52935         c.on("dblclick", this.onDblClick, this);
52936         c.on("contextmenu", this.onContextMenu, this);
52937         c.on("keydown", this.onKeyDown, this);
52938         if (Roo.isTouch) {
52939             c.on("touchstart", this.onTouchStart, this);
52940         }
52941
52942         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52943
52944         this.getSelectionModel().init(this);
52945
52946         view.render();
52947
52948         if(this.loadMask){
52949             this.loadMask = new Roo.LoadMask(this.container,
52950                     Roo.apply({store:this.dataSource}, this.loadMask));
52951         }
52952         
52953         
52954         if (this.toolbar && this.toolbar.xtype) {
52955             this.toolbar.container = this.getView().getHeaderPanel(true);
52956             this.toolbar = new Roo.Toolbar(this.toolbar);
52957         }
52958         if (this.footer && this.footer.xtype) {
52959             this.footer.dataSource = this.getDataSource();
52960             this.footer.container = this.getView().getFooterPanel(true);
52961             this.footer = Roo.factory(this.footer, Roo);
52962         }
52963         if (this.dropTarget && this.dropTarget.xtype) {
52964             delete this.dropTarget.xtype;
52965             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52966         }
52967         
52968         
52969         this.rendered = true;
52970         this.fireEvent('render', this);
52971         return this;
52972     },
52973
52974         /**
52975          * Reconfigures the grid to use a different Store and Column Model.
52976          * The View will be bound to the new objects and refreshed.
52977          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52978          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52979          */
52980     reconfigure : function(dataSource, colModel){
52981         if(this.loadMask){
52982             this.loadMask.destroy();
52983             this.loadMask = new Roo.LoadMask(this.container,
52984                     Roo.apply({store:dataSource}, this.loadMask));
52985         }
52986         this.view.bind(dataSource, colModel);
52987         this.dataSource = dataSource;
52988         this.colModel = colModel;
52989         this.view.refresh(true);
52990     },
52991
52992     // private
52993     onKeyDown : function(e){
52994         this.fireEvent("keydown", e);
52995     },
52996
52997     /**
52998      * Destroy this grid.
52999      * @param {Boolean} removeEl True to remove the element
53000      */
53001     destroy : function(removeEl, keepListeners){
53002         if(this.loadMask){
53003             this.loadMask.destroy();
53004         }
53005         var c = this.container;
53006         c.removeAllListeners();
53007         this.view.destroy();
53008         this.colModel.purgeListeners();
53009         if(!keepListeners){
53010             this.purgeListeners();
53011         }
53012         c.update("");
53013         if(removeEl === true){
53014             c.remove();
53015         }
53016     },
53017
53018     // private
53019     processEvent : function(name, e){
53020         // does this fire select???
53021         //Roo.log('grid:processEvent '  + name);
53022         
53023         if (name != 'touchstart' ) {
53024             this.fireEvent(name, e);    
53025         }
53026         
53027         var t = e.getTarget();
53028         var v = this.view;
53029         var header = v.findHeaderIndex(t);
53030         if(header !== false){
53031             var ename = name == 'touchstart' ? 'click' : name;
53032              
53033             this.fireEvent("header" + ename, this, header, e);
53034         }else{
53035             var row = v.findRowIndex(t);
53036             var cell = v.findCellIndex(t);
53037             if (name == 'touchstart') {
53038                 // first touch is always a click.
53039                 // hopefull this happens after selection is updated.?
53040                 name = false;
53041                 
53042                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
53043                     var cs = this.selModel.getSelectedCell();
53044                     if (row == cs[0] && cell == cs[1]){
53045                         name = 'dblclick';
53046                     }
53047                 }
53048                 if (typeof(this.selModel.getSelections) != 'undefined') {
53049                     var cs = this.selModel.getSelections();
53050                     var ds = this.dataSource;
53051                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
53052                         name = 'dblclick';
53053                     }
53054                 }
53055                 if (!name) {
53056                     return;
53057                 }
53058             }
53059             
53060             
53061             if(row !== false){
53062                 this.fireEvent("row" + name, this, row, e);
53063                 if(cell !== false){
53064                     this.fireEvent("cell" + name, this, row, cell, e);
53065                 }
53066             }
53067         }
53068     },
53069
53070     // private
53071     onClick : function(e){
53072         this.processEvent("click", e);
53073     },
53074    // private
53075     onTouchStart : function(e){
53076         this.processEvent("touchstart", e);
53077     },
53078
53079     // private
53080     onContextMenu : function(e, t){
53081         this.processEvent("contextmenu", e);
53082     },
53083
53084     // private
53085     onDblClick : function(e){
53086         this.processEvent("dblclick", e);
53087     },
53088
53089     // private
53090     walkCells : function(row, col, step, fn, scope){
53091         var cm = this.colModel, clen = cm.getColumnCount();
53092         var ds = this.dataSource, rlen = ds.getCount(), first = true;
53093         if(step < 0){
53094             if(col < 0){
53095                 row--;
53096                 first = false;
53097             }
53098             while(row >= 0){
53099                 if(!first){
53100                     col = clen-1;
53101                 }
53102                 first = false;
53103                 while(col >= 0){
53104                     if(fn.call(scope || this, row, col, cm) === true){
53105                         return [row, col];
53106                     }
53107                     col--;
53108                 }
53109                 row--;
53110             }
53111         } else {
53112             if(col >= clen){
53113                 row++;
53114                 first = false;
53115             }
53116             while(row < rlen){
53117                 if(!first){
53118                     col = 0;
53119                 }
53120                 first = false;
53121                 while(col < clen){
53122                     if(fn.call(scope || this, row, col, cm) === true){
53123                         return [row, col];
53124                     }
53125                     col++;
53126                 }
53127                 row++;
53128             }
53129         }
53130         return null;
53131     },
53132
53133     // private
53134     getSelections : function(){
53135         return this.selModel.getSelections();
53136     },
53137
53138     /**
53139      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
53140      * but if manual update is required this method will initiate it.
53141      */
53142     autoSize : function(){
53143         if(this.rendered){
53144             this.view.layout();
53145             if(this.view.adjustForScroll){
53146                 this.view.adjustForScroll();
53147             }
53148         }
53149     },
53150
53151     /**
53152      * Returns the grid's underlying element.
53153      * @return {Element} The element
53154      */
53155     getGridEl : function(){
53156         return this.container;
53157     },
53158
53159     // private for compatibility, overridden by editor grid
53160     stopEditing : function(){},
53161
53162     /**
53163      * Returns the grid's SelectionModel.
53164      * @return {SelectionModel}
53165      */
53166     getSelectionModel : function(){
53167         if(!this.selModel){
53168             this.selModel = new Roo.grid.RowSelectionModel();
53169         }
53170         return this.selModel;
53171     },
53172
53173     /**
53174      * Returns the grid's DataSource.
53175      * @return {DataSource}
53176      */
53177     getDataSource : function(){
53178         return this.dataSource;
53179     },
53180
53181     /**
53182      * Returns the grid's ColumnModel.
53183      * @return {ColumnModel}
53184      */
53185     getColumnModel : function(){
53186         return this.colModel;
53187     },
53188
53189     /**
53190      * Returns the grid's GridView object.
53191      * @return {GridView}
53192      */
53193     getView : function(){
53194         if(!this.view){
53195             this.view = new Roo.grid.GridView(this.viewConfig);
53196         }
53197         return this.view;
53198     },
53199     /**
53200      * Called to get grid's drag proxy text, by default returns this.ddText.
53201      * @return {String}
53202      */
53203     getDragDropText : function(){
53204         var count = this.selModel.getCount();
53205         return String.format(this.ddText, count, count == 1 ? '' : 's');
53206     }
53207 });
53208 /**
53209  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53210  * %0 is replaced with the number of selected rows.
53211  * @type String
53212  */
53213 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53214  * Based on:
53215  * Ext JS Library 1.1.1
53216  * Copyright(c) 2006-2007, Ext JS, LLC.
53217  *
53218  * Originally Released Under LGPL - original licence link has changed is not relivant.
53219  *
53220  * Fork - LGPL
53221  * <script type="text/javascript">
53222  */
53223  
53224 Roo.grid.AbstractGridView = function(){
53225         this.grid = null;
53226         
53227         this.events = {
53228             "beforerowremoved" : true,
53229             "beforerowsinserted" : true,
53230             "beforerefresh" : true,
53231             "rowremoved" : true,
53232             "rowsinserted" : true,
53233             "rowupdated" : true,
53234             "refresh" : true
53235         };
53236     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53237 };
53238
53239 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53240     rowClass : "x-grid-row",
53241     cellClass : "x-grid-cell",
53242     tdClass : "x-grid-td",
53243     hdClass : "x-grid-hd",
53244     splitClass : "x-grid-hd-split",
53245     
53246     init: function(grid){
53247         this.grid = grid;
53248                 var cid = this.grid.getGridEl().id;
53249         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53250         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53251         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53252         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53253         },
53254         
53255     getColumnRenderers : function(){
53256         var renderers = [];
53257         var cm = this.grid.colModel;
53258         var colCount = cm.getColumnCount();
53259         for(var i = 0; i < colCount; i++){
53260             renderers[i] = cm.getRenderer(i);
53261         }
53262         return renderers;
53263     },
53264     
53265     getColumnIds : function(){
53266         var ids = [];
53267         var cm = this.grid.colModel;
53268         var colCount = cm.getColumnCount();
53269         for(var i = 0; i < colCount; i++){
53270             ids[i] = cm.getColumnId(i);
53271         }
53272         return ids;
53273     },
53274     
53275     getDataIndexes : function(){
53276         if(!this.indexMap){
53277             this.indexMap = this.buildIndexMap();
53278         }
53279         return this.indexMap.colToData;
53280     },
53281     
53282     getColumnIndexByDataIndex : function(dataIndex){
53283         if(!this.indexMap){
53284             this.indexMap = this.buildIndexMap();
53285         }
53286         return this.indexMap.dataToCol[dataIndex];
53287     },
53288     
53289     /**
53290      * Set a css style for a column dynamically. 
53291      * @param {Number} colIndex The index of the column
53292      * @param {String} name The css property name
53293      * @param {String} value The css value
53294      */
53295     setCSSStyle : function(colIndex, name, value){
53296         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53297         Roo.util.CSS.updateRule(selector, name, value);
53298     },
53299     
53300     generateRules : function(cm){
53301         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53302         Roo.util.CSS.removeStyleSheet(rulesId);
53303         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53304             var cid = cm.getColumnId(i);
53305             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53306                          this.tdSelector, cid, " {\n}\n",
53307                          this.hdSelector, cid, " {\n}\n",
53308                          this.splitSelector, cid, " {\n}\n");
53309         }
53310         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53311     }
53312 });/*
53313  * Based on:
53314  * Ext JS Library 1.1.1
53315  * Copyright(c) 2006-2007, Ext JS, LLC.
53316  *
53317  * Originally Released Under LGPL - original licence link has changed is not relivant.
53318  *
53319  * Fork - LGPL
53320  * <script type="text/javascript">
53321  */
53322
53323 // private
53324 // This is a support class used internally by the Grid components
53325 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53326     this.grid = grid;
53327     this.view = grid.getView();
53328     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53329     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53330     if(hd2){
53331         this.setHandleElId(Roo.id(hd));
53332         this.setOuterHandleElId(Roo.id(hd2));
53333     }
53334     this.scroll = false;
53335 };
53336 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53337     maxDragWidth: 120,
53338     getDragData : function(e){
53339         var t = Roo.lib.Event.getTarget(e);
53340         var h = this.view.findHeaderCell(t);
53341         if(h){
53342             return {ddel: h.firstChild, header:h};
53343         }
53344         return false;
53345     },
53346
53347     onInitDrag : function(e){
53348         this.view.headersDisabled = true;
53349         var clone = this.dragData.ddel.cloneNode(true);
53350         clone.id = Roo.id();
53351         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53352         this.proxy.update(clone);
53353         return true;
53354     },
53355
53356     afterValidDrop : function(){
53357         var v = this.view;
53358         setTimeout(function(){
53359             v.headersDisabled = false;
53360         }, 50);
53361     },
53362
53363     afterInvalidDrop : function(){
53364         var v = this.view;
53365         setTimeout(function(){
53366             v.headersDisabled = false;
53367         }, 50);
53368     }
53369 });
53370 /*
53371  * Based on:
53372  * Ext JS Library 1.1.1
53373  * Copyright(c) 2006-2007, Ext JS, LLC.
53374  *
53375  * Originally Released Under LGPL - original licence link has changed is not relivant.
53376  *
53377  * Fork - LGPL
53378  * <script type="text/javascript">
53379  */
53380 // private
53381 // This is a support class used internally by the Grid components
53382 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53383     this.grid = grid;
53384     this.view = grid.getView();
53385     // split the proxies so they don't interfere with mouse events
53386     this.proxyTop = Roo.DomHelper.append(document.body, {
53387         cls:"col-move-top", html:"&#160;"
53388     }, true);
53389     this.proxyBottom = Roo.DomHelper.append(document.body, {
53390         cls:"col-move-bottom", html:"&#160;"
53391     }, true);
53392     this.proxyTop.hide = this.proxyBottom.hide = function(){
53393         this.setLeftTop(-100,-100);
53394         this.setStyle("visibility", "hidden");
53395     };
53396     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53397     // temporarily disabled
53398     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53399     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53400 };
53401 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53402     proxyOffsets : [-4, -9],
53403     fly: Roo.Element.fly,
53404
53405     getTargetFromEvent : function(e){
53406         var t = Roo.lib.Event.getTarget(e);
53407         var cindex = this.view.findCellIndex(t);
53408         if(cindex !== false){
53409             return this.view.getHeaderCell(cindex);
53410         }
53411         return null;
53412     },
53413
53414     nextVisible : function(h){
53415         var v = this.view, cm = this.grid.colModel;
53416         h = h.nextSibling;
53417         while(h){
53418             if(!cm.isHidden(v.getCellIndex(h))){
53419                 return h;
53420             }
53421             h = h.nextSibling;
53422         }
53423         return null;
53424     },
53425
53426     prevVisible : function(h){
53427         var v = this.view, cm = this.grid.colModel;
53428         h = h.prevSibling;
53429         while(h){
53430             if(!cm.isHidden(v.getCellIndex(h))){
53431                 return h;
53432             }
53433             h = h.prevSibling;
53434         }
53435         return null;
53436     },
53437
53438     positionIndicator : function(h, n, e){
53439         var x = Roo.lib.Event.getPageX(e);
53440         var r = Roo.lib.Dom.getRegion(n.firstChild);
53441         var px, pt, py = r.top + this.proxyOffsets[1];
53442         if((r.right - x) <= (r.right-r.left)/2){
53443             px = r.right+this.view.borderWidth;
53444             pt = "after";
53445         }else{
53446             px = r.left;
53447             pt = "before";
53448         }
53449         var oldIndex = this.view.getCellIndex(h);
53450         var newIndex = this.view.getCellIndex(n);
53451
53452         if(this.grid.colModel.isFixed(newIndex)){
53453             return false;
53454         }
53455
53456         var locked = this.grid.colModel.isLocked(newIndex);
53457
53458         if(pt == "after"){
53459             newIndex++;
53460         }
53461         if(oldIndex < newIndex){
53462             newIndex--;
53463         }
53464         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53465             return false;
53466         }
53467         px +=  this.proxyOffsets[0];
53468         this.proxyTop.setLeftTop(px, py);
53469         this.proxyTop.show();
53470         if(!this.bottomOffset){
53471             this.bottomOffset = this.view.mainHd.getHeight();
53472         }
53473         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53474         this.proxyBottom.show();
53475         return pt;
53476     },
53477
53478     onNodeEnter : function(n, dd, e, data){
53479         if(data.header != n){
53480             this.positionIndicator(data.header, n, e);
53481         }
53482     },
53483
53484     onNodeOver : function(n, dd, e, data){
53485         var result = false;
53486         if(data.header != n){
53487             result = this.positionIndicator(data.header, n, e);
53488         }
53489         if(!result){
53490             this.proxyTop.hide();
53491             this.proxyBottom.hide();
53492         }
53493         return result ? this.dropAllowed : this.dropNotAllowed;
53494     },
53495
53496     onNodeOut : function(n, dd, e, data){
53497         this.proxyTop.hide();
53498         this.proxyBottom.hide();
53499     },
53500
53501     onNodeDrop : function(n, dd, e, data){
53502         var h = data.header;
53503         if(h != n){
53504             var cm = this.grid.colModel;
53505             var x = Roo.lib.Event.getPageX(e);
53506             var r = Roo.lib.Dom.getRegion(n.firstChild);
53507             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53508             var oldIndex = this.view.getCellIndex(h);
53509             var newIndex = this.view.getCellIndex(n);
53510             var locked = cm.isLocked(newIndex);
53511             if(pt == "after"){
53512                 newIndex++;
53513             }
53514             if(oldIndex < newIndex){
53515                 newIndex--;
53516             }
53517             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53518                 return false;
53519             }
53520             cm.setLocked(oldIndex, locked, true);
53521             cm.moveColumn(oldIndex, newIndex);
53522             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53523             return true;
53524         }
53525         return false;
53526     }
53527 });
53528 /*
53529  * Based on:
53530  * Ext JS Library 1.1.1
53531  * Copyright(c) 2006-2007, Ext JS, LLC.
53532  *
53533  * Originally Released Under LGPL - original licence link has changed is not relivant.
53534  *
53535  * Fork - LGPL
53536  * <script type="text/javascript">
53537  */
53538   
53539 /**
53540  * @class Roo.grid.GridView
53541  * @extends Roo.util.Observable
53542  *
53543  * @constructor
53544  * @param {Object} config
53545  */
53546 Roo.grid.GridView = function(config){
53547     Roo.grid.GridView.superclass.constructor.call(this);
53548     this.el = null;
53549
53550     Roo.apply(this, config);
53551 };
53552
53553 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53554
53555     unselectable :  'unselectable="on"',
53556     unselectableCls :  'x-unselectable',
53557     
53558     
53559     rowClass : "x-grid-row",
53560
53561     cellClass : "x-grid-col",
53562
53563     tdClass : "x-grid-td",
53564
53565     hdClass : "x-grid-hd",
53566
53567     splitClass : "x-grid-split",
53568
53569     sortClasses : ["sort-asc", "sort-desc"],
53570
53571     enableMoveAnim : false,
53572
53573     hlColor: "C3DAF9",
53574
53575     dh : Roo.DomHelper,
53576
53577     fly : Roo.Element.fly,
53578
53579     css : Roo.util.CSS,
53580
53581     borderWidth: 1,
53582
53583     splitOffset: 3,
53584
53585     scrollIncrement : 22,
53586
53587     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53588
53589     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53590
53591     bind : function(ds, cm){
53592         if(this.ds){
53593             this.ds.un("load", this.onLoad, this);
53594             this.ds.un("datachanged", this.onDataChange, this);
53595             this.ds.un("add", this.onAdd, this);
53596             this.ds.un("remove", this.onRemove, this);
53597             this.ds.un("update", this.onUpdate, this);
53598             this.ds.un("clear", this.onClear, this);
53599         }
53600         if(ds){
53601             ds.on("load", this.onLoad, this);
53602             ds.on("datachanged", this.onDataChange, this);
53603             ds.on("add", this.onAdd, this);
53604             ds.on("remove", this.onRemove, this);
53605             ds.on("update", this.onUpdate, this);
53606             ds.on("clear", this.onClear, this);
53607         }
53608         this.ds = ds;
53609
53610         if(this.cm){
53611             this.cm.un("widthchange", this.onColWidthChange, this);
53612             this.cm.un("headerchange", this.onHeaderChange, this);
53613             this.cm.un("hiddenchange", this.onHiddenChange, this);
53614             this.cm.un("columnmoved", this.onColumnMove, this);
53615             this.cm.un("columnlockchange", this.onColumnLock, this);
53616         }
53617         if(cm){
53618             this.generateRules(cm);
53619             cm.on("widthchange", this.onColWidthChange, this);
53620             cm.on("headerchange", this.onHeaderChange, this);
53621             cm.on("hiddenchange", this.onHiddenChange, this);
53622             cm.on("columnmoved", this.onColumnMove, this);
53623             cm.on("columnlockchange", this.onColumnLock, this);
53624         }
53625         this.cm = cm;
53626     },
53627
53628     init: function(grid){
53629         Roo.grid.GridView.superclass.init.call(this, grid);
53630
53631         this.bind(grid.dataSource, grid.colModel);
53632
53633         grid.on("headerclick", this.handleHeaderClick, this);
53634
53635         if(grid.trackMouseOver){
53636             grid.on("mouseover", this.onRowOver, this);
53637             grid.on("mouseout", this.onRowOut, this);
53638         }
53639         grid.cancelTextSelection = function(){};
53640         this.gridId = grid.id;
53641
53642         var tpls = this.templates || {};
53643
53644         if(!tpls.master){
53645             tpls.master = new Roo.Template(
53646                '<div class="x-grid" hidefocus="true">',
53647                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53648                   '<div class="x-grid-topbar"></div>',
53649                   '<div class="x-grid-scroller"><div></div></div>',
53650                   '<div class="x-grid-locked">',
53651                       '<div class="x-grid-header">{lockedHeader}</div>',
53652                       '<div class="x-grid-body">{lockedBody}</div>',
53653                   "</div>",
53654                   '<div class="x-grid-viewport">',
53655                       '<div class="x-grid-header">{header}</div>',
53656                       '<div class="x-grid-body">{body}</div>',
53657                   "</div>",
53658                   '<div class="x-grid-bottombar"></div>',
53659                  
53660                   '<div class="x-grid-resize-proxy">&#160;</div>',
53661                "</div>"
53662             );
53663             tpls.master.disableformats = true;
53664         }
53665
53666         if(!tpls.header){
53667             tpls.header = new Roo.Template(
53668                '<table border="0" cellspacing="0" cellpadding="0">',
53669                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53670                "</table>{splits}"
53671             );
53672             tpls.header.disableformats = true;
53673         }
53674         tpls.header.compile();
53675
53676         if(!tpls.hcell){
53677             tpls.hcell = new Roo.Template(
53678                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53679                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53680                 "</div></td>"
53681              );
53682              tpls.hcell.disableFormats = true;
53683         }
53684         tpls.hcell.compile();
53685
53686         if(!tpls.hsplit){
53687             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53688                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53689             tpls.hsplit.disableFormats = true;
53690         }
53691         tpls.hsplit.compile();
53692
53693         if(!tpls.body){
53694             tpls.body = new Roo.Template(
53695                '<table border="0" cellspacing="0" cellpadding="0">',
53696                "<tbody>{rows}</tbody>",
53697                "</table>"
53698             );
53699             tpls.body.disableFormats = true;
53700         }
53701         tpls.body.compile();
53702
53703         if(!tpls.row){
53704             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53705             tpls.row.disableFormats = true;
53706         }
53707         tpls.row.compile();
53708
53709         if(!tpls.cell){
53710             tpls.cell = new Roo.Template(
53711                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53712                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53713                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53714                 "</td>"
53715             );
53716             tpls.cell.disableFormats = true;
53717         }
53718         tpls.cell.compile();
53719
53720         this.templates = tpls;
53721     },
53722
53723     // remap these for backwards compat
53724     onColWidthChange : function(){
53725         this.updateColumns.apply(this, arguments);
53726     },
53727     onHeaderChange : function(){
53728         this.updateHeaders.apply(this, arguments);
53729     }, 
53730     onHiddenChange : function(){
53731         this.handleHiddenChange.apply(this, arguments);
53732     },
53733     onColumnMove : function(){
53734         this.handleColumnMove.apply(this, arguments);
53735     },
53736     onColumnLock : function(){
53737         this.handleLockChange.apply(this, arguments);
53738     },
53739
53740     onDataChange : function(){
53741         this.refresh();
53742         this.updateHeaderSortState();
53743     },
53744
53745     onClear : function(){
53746         this.refresh();
53747     },
53748
53749     onUpdate : function(ds, record){
53750         this.refreshRow(record);
53751     },
53752
53753     refreshRow : function(record){
53754         var ds = this.ds, index;
53755         if(typeof record == 'number'){
53756             index = record;
53757             record = ds.getAt(index);
53758         }else{
53759             index = ds.indexOf(record);
53760         }
53761         this.insertRows(ds, index, index, true);
53762         this.onRemove(ds, record, index+1, true);
53763         this.syncRowHeights(index, index);
53764         this.layout();
53765         this.fireEvent("rowupdated", this, index, record);
53766     },
53767
53768     onAdd : function(ds, records, index){
53769         this.insertRows(ds, index, index + (records.length-1));
53770     },
53771
53772     onRemove : function(ds, record, index, isUpdate){
53773         if(isUpdate !== true){
53774             this.fireEvent("beforerowremoved", this, index, record);
53775         }
53776         var bt = this.getBodyTable(), lt = this.getLockedTable();
53777         if(bt.rows[index]){
53778             bt.firstChild.removeChild(bt.rows[index]);
53779         }
53780         if(lt.rows[index]){
53781             lt.firstChild.removeChild(lt.rows[index]);
53782         }
53783         if(isUpdate !== true){
53784             this.stripeRows(index);
53785             this.syncRowHeights(index, index);
53786             this.layout();
53787             this.fireEvent("rowremoved", this, index, record);
53788         }
53789     },
53790
53791     onLoad : function(){
53792         this.scrollToTop();
53793     },
53794
53795     /**
53796      * Scrolls the grid to the top
53797      */
53798     scrollToTop : function(){
53799         if(this.scroller){
53800             this.scroller.dom.scrollTop = 0;
53801             this.syncScroll();
53802         }
53803     },
53804
53805     /**
53806      * Gets a panel in the header of the grid that can be used for toolbars etc.
53807      * After modifying the contents of this panel a call to grid.autoSize() may be
53808      * required to register any changes in size.
53809      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53810      * @return Roo.Element
53811      */
53812     getHeaderPanel : function(doShow){
53813         if(doShow){
53814             this.headerPanel.show();
53815         }
53816         return this.headerPanel;
53817     },
53818
53819     /**
53820      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53821      * After modifying the contents of this panel a call to grid.autoSize() may be
53822      * required to register any changes in size.
53823      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53824      * @return Roo.Element
53825      */
53826     getFooterPanel : function(doShow){
53827         if(doShow){
53828             this.footerPanel.show();
53829         }
53830         return this.footerPanel;
53831     },
53832
53833     initElements : function(){
53834         var E = Roo.Element;
53835         var el = this.grid.getGridEl().dom.firstChild;
53836         var cs = el.childNodes;
53837
53838         this.el = new E(el);
53839         
53840          this.focusEl = new E(el.firstChild);
53841         this.focusEl.swallowEvent("click", true);
53842         
53843         this.headerPanel = new E(cs[1]);
53844         this.headerPanel.enableDisplayMode("block");
53845
53846         this.scroller = new E(cs[2]);
53847         this.scrollSizer = new E(this.scroller.dom.firstChild);
53848
53849         this.lockedWrap = new E(cs[3]);
53850         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53851         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53852
53853         this.mainWrap = new E(cs[4]);
53854         this.mainHd = new E(this.mainWrap.dom.firstChild);
53855         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53856
53857         this.footerPanel = new E(cs[5]);
53858         this.footerPanel.enableDisplayMode("block");
53859
53860         this.resizeProxy = new E(cs[6]);
53861
53862         this.headerSelector = String.format(
53863            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53864            this.lockedHd.id, this.mainHd.id
53865         );
53866
53867         this.splitterSelector = String.format(
53868            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53869            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53870         );
53871     },
53872     idToCssName : function(s)
53873     {
53874         return s.replace(/[^a-z0-9]+/ig, '-');
53875     },
53876
53877     getHeaderCell : function(index){
53878         return Roo.DomQuery.select(this.headerSelector)[index];
53879     },
53880
53881     getHeaderCellMeasure : function(index){
53882         return this.getHeaderCell(index).firstChild;
53883     },
53884
53885     getHeaderCellText : function(index){
53886         return this.getHeaderCell(index).firstChild.firstChild;
53887     },
53888
53889     getLockedTable : function(){
53890         return this.lockedBody.dom.firstChild;
53891     },
53892
53893     getBodyTable : function(){
53894         return this.mainBody.dom.firstChild;
53895     },
53896
53897     getLockedRow : function(index){
53898         return this.getLockedTable().rows[index];
53899     },
53900
53901     getRow : function(index){
53902         return this.getBodyTable().rows[index];
53903     },
53904
53905     getRowComposite : function(index){
53906         if(!this.rowEl){
53907             this.rowEl = new Roo.CompositeElementLite();
53908         }
53909         var els = [], lrow, mrow;
53910         if(lrow = this.getLockedRow(index)){
53911             els.push(lrow);
53912         }
53913         if(mrow = this.getRow(index)){
53914             els.push(mrow);
53915         }
53916         this.rowEl.elements = els;
53917         return this.rowEl;
53918     },
53919     /**
53920      * Gets the 'td' of the cell
53921      * 
53922      * @param {Integer} rowIndex row to select
53923      * @param {Integer} colIndex column to select
53924      * 
53925      * @return {Object} 
53926      */
53927     getCell : function(rowIndex, colIndex){
53928         var locked = this.cm.getLockedCount();
53929         var source;
53930         if(colIndex < locked){
53931             source = this.lockedBody.dom.firstChild;
53932         }else{
53933             source = this.mainBody.dom.firstChild;
53934             colIndex -= locked;
53935         }
53936         return source.rows[rowIndex].childNodes[colIndex];
53937     },
53938
53939     getCellText : function(rowIndex, colIndex){
53940         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53941     },
53942
53943     getCellBox : function(cell){
53944         var b = this.fly(cell).getBox();
53945         if(Roo.isOpera){ // opera fails to report the Y
53946             b.y = cell.offsetTop + this.mainBody.getY();
53947         }
53948         return b;
53949     },
53950
53951     getCellIndex : function(cell){
53952         var id = String(cell.className).match(this.cellRE);
53953         if(id){
53954             return parseInt(id[1], 10);
53955         }
53956         return 0;
53957     },
53958
53959     findHeaderIndex : function(n){
53960         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53961         return r ? this.getCellIndex(r) : false;
53962     },
53963
53964     findHeaderCell : function(n){
53965         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53966         return r ? r : false;
53967     },
53968
53969     findRowIndex : function(n){
53970         if(!n){
53971             return false;
53972         }
53973         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53974         return r ? r.rowIndex : false;
53975     },
53976
53977     findCellIndex : function(node){
53978         var stop = this.el.dom;
53979         while(node && node != stop){
53980             if(this.findRE.test(node.className)){
53981                 return this.getCellIndex(node);
53982             }
53983             node = node.parentNode;
53984         }
53985         return false;
53986     },
53987
53988     getColumnId : function(index){
53989         return this.cm.getColumnId(index);
53990     },
53991
53992     getSplitters : function()
53993     {
53994         if(this.splitterSelector){
53995            return Roo.DomQuery.select(this.splitterSelector);
53996         }else{
53997             return null;
53998       }
53999     },
54000
54001     getSplitter : function(index){
54002         return this.getSplitters()[index];
54003     },
54004
54005     onRowOver : function(e, t){
54006         var row;
54007         if((row = this.findRowIndex(t)) !== false){
54008             this.getRowComposite(row).addClass("x-grid-row-over");
54009         }
54010     },
54011
54012     onRowOut : function(e, t){
54013         var row;
54014         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
54015             this.getRowComposite(row).removeClass("x-grid-row-over");
54016         }
54017     },
54018
54019     renderHeaders : function(){
54020         var cm = this.cm;
54021         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
54022         var cb = [], lb = [], sb = [], lsb = [], p = {};
54023         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54024             p.cellId = "x-grid-hd-0-" + i;
54025             p.splitId = "x-grid-csplit-0-" + i;
54026             p.id = cm.getColumnId(i);
54027             p.value = cm.getColumnHeader(i) || "";
54028             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
54029             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
54030             if(!cm.isLocked(i)){
54031                 cb[cb.length] = ct.apply(p);
54032                 sb[sb.length] = st.apply(p);
54033             }else{
54034                 lb[lb.length] = ct.apply(p);
54035                 lsb[lsb.length] = st.apply(p);
54036             }
54037         }
54038         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
54039                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
54040     },
54041
54042     updateHeaders : function(){
54043         var html = this.renderHeaders();
54044         this.lockedHd.update(html[0]);
54045         this.mainHd.update(html[1]);
54046     },
54047
54048     /**
54049      * Focuses the specified row.
54050      * @param {Number} row The row index
54051      */
54052     focusRow : function(row)
54053     {
54054         //Roo.log('GridView.focusRow');
54055         var x = this.scroller.dom.scrollLeft;
54056         this.focusCell(row, 0, false);
54057         this.scroller.dom.scrollLeft = x;
54058     },
54059
54060     /**
54061      * Focuses the specified cell.
54062      * @param {Number} row The row index
54063      * @param {Number} col The column index
54064      * @param {Boolean} hscroll false to disable horizontal scrolling
54065      */
54066     focusCell : function(row, col, hscroll)
54067     {
54068         //Roo.log('GridView.focusCell');
54069         var el = this.ensureVisible(row, col, hscroll);
54070         this.focusEl.alignTo(el, "tl-tl");
54071         if(Roo.isGecko){
54072             this.focusEl.focus();
54073         }else{
54074             this.focusEl.focus.defer(1, this.focusEl);
54075         }
54076     },
54077
54078     /**
54079      * Scrolls the specified cell into view
54080      * @param {Number} row The row index
54081      * @param {Number} col The column index
54082      * @param {Boolean} hscroll false to disable horizontal scrolling
54083      */
54084     ensureVisible : function(row, col, hscroll)
54085     {
54086         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
54087         //return null; //disable for testing.
54088         if(typeof row != "number"){
54089             row = row.rowIndex;
54090         }
54091         if(row < 0 && row >= this.ds.getCount()){
54092             return  null;
54093         }
54094         col = (col !== undefined ? col : 0);
54095         var cm = this.grid.colModel;
54096         while(cm.isHidden(col)){
54097             col++;
54098         }
54099
54100         var el = this.getCell(row, col);
54101         if(!el){
54102             return null;
54103         }
54104         var c = this.scroller.dom;
54105
54106         var ctop = parseInt(el.offsetTop, 10);
54107         var cleft = parseInt(el.offsetLeft, 10);
54108         var cbot = ctop + el.offsetHeight;
54109         var cright = cleft + el.offsetWidth;
54110         
54111         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
54112         var stop = parseInt(c.scrollTop, 10);
54113         var sleft = parseInt(c.scrollLeft, 10);
54114         var sbot = stop + ch;
54115         var sright = sleft + c.clientWidth;
54116         /*
54117         Roo.log('GridView.ensureVisible:' +
54118                 ' ctop:' + ctop +
54119                 ' c.clientHeight:' + c.clientHeight +
54120                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
54121                 ' stop:' + stop +
54122                 ' cbot:' + cbot +
54123                 ' sbot:' + sbot +
54124                 ' ch:' + ch  
54125                 );
54126         */
54127         if(ctop < stop){
54128              c.scrollTop = ctop;
54129             //Roo.log("set scrolltop to ctop DISABLE?");
54130         }else if(cbot > sbot){
54131             //Roo.log("set scrolltop to cbot-ch");
54132             c.scrollTop = cbot-ch;
54133         }
54134         
54135         if(hscroll !== false){
54136             if(cleft < sleft){
54137                 c.scrollLeft = cleft;
54138             }else if(cright > sright){
54139                 c.scrollLeft = cright-c.clientWidth;
54140             }
54141         }
54142          
54143         return el;
54144     },
54145
54146     updateColumns : function(){
54147         this.grid.stopEditing();
54148         var cm = this.grid.colModel, colIds = this.getColumnIds();
54149         //var totalWidth = cm.getTotalWidth();
54150         var pos = 0;
54151         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54152             //if(cm.isHidden(i)) continue;
54153             var w = cm.getColumnWidth(i);
54154             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54155             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54156         }
54157         this.updateSplitters();
54158     },
54159
54160     generateRules : function(cm){
54161         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54162         Roo.util.CSS.removeStyleSheet(rulesId);
54163         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54164             var cid = cm.getColumnId(i);
54165             var align = '';
54166             if(cm.config[i].align){
54167                 align = 'text-align:'+cm.config[i].align+';';
54168             }
54169             var hidden = '';
54170             if(cm.isHidden(i)){
54171                 hidden = 'display:none;';
54172             }
54173             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54174             ruleBuf.push(
54175                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54176                     this.hdSelector, cid, " {\n", align, width, "}\n",
54177                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54178                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54179         }
54180         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54181     },
54182
54183     updateSplitters : function(){
54184         var cm = this.cm, s = this.getSplitters();
54185         if(s){ // splitters not created yet
54186             var pos = 0, locked = true;
54187             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54188                 if(cm.isHidden(i)) {
54189                     continue;
54190                 }
54191                 var w = cm.getColumnWidth(i); // make sure it's a number
54192                 if(!cm.isLocked(i) && locked){
54193                     pos = 0;
54194                     locked = false;
54195                 }
54196                 pos += w;
54197                 s[i].style.left = (pos-this.splitOffset) + "px";
54198             }
54199         }
54200     },
54201
54202     handleHiddenChange : function(colModel, colIndex, hidden){
54203         if(hidden){
54204             this.hideColumn(colIndex);
54205         }else{
54206             this.unhideColumn(colIndex);
54207         }
54208     },
54209
54210     hideColumn : function(colIndex){
54211         var cid = this.getColumnId(colIndex);
54212         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54213         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54214         if(Roo.isSafari){
54215             this.updateHeaders();
54216         }
54217         this.updateSplitters();
54218         this.layout();
54219     },
54220
54221     unhideColumn : function(colIndex){
54222         var cid = this.getColumnId(colIndex);
54223         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54224         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54225
54226         if(Roo.isSafari){
54227             this.updateHeaders();
54228         }
54229         this.updateSplitters();
54230         this.layout();
54231     },
54232
54233     insertRows : function(dm, firstRow, lastRow, isUpdate){
54234         if(firstRow == 0 && lastRow == dm.getCount()-1){
54235             this.refresh();
54236         }else{
54237             if(!isUpdate){
54238                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54239             }
54240             var s = this.getScrollState();
54241             var markup = this.renderRows(firstRow, lastRow);
54242             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54243             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54244             this.restoreScroll(s);
54245             if(!isUpdate){
54246                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54247                 this.syncRowHeights(firstRow, lastRow);
54248                 this.stripeRows(firstRow);
54249                 this.layout();
54250             }
54251         }
54252     },
54253
54254     bufferRows : function(markup, target, index){
54255         var before = null, trows = target.rows, tbody = target.tBodies[0];
54256         if(index < trows.length){
54257             before = trows[index];
54258         }
54259         var b = document.createElement("div");
54260         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54261         var rows = b.firstChild.rows;
54262         for(var i = 0, len = rows.length; i < len; i++){
54263             if(before){
54264                 tbody.insertBefore(rows[0], before);
54265             }else{
54266                 tbody.appendChild(rows[0]);
54267             }
54268         }
54269         b.innerHTML = "";
54270         b = null;
54271     },
54272
54273     deleteRows : function(dm, firstRow, lastRow){
54274         if(dm.getRowCount()<1){
54275             this.fireEvent("beforerefresh", this);
54276             this.mainBody.update("");
54277             this.lockedBody.update("");
54278             this.fireEvent("refresh", this);
54279         }else{
54280             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54281             var bt = this.getBodyTable();
54282             var tbody = bt.firstChild;
54283             var rows = bt.rows;
54284             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54285                 tbody.removeChild(rows[firstRow]);
54286             }
54287             this.stripeRows(firstRow);
54288             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54289         }
54290     },
54291
54292     updateRows : function(dataSource, firstRow, lastRow){
54293         var s = this.getScrollState();
54294         this.refresh();
54295         this.restoreScroll(s);
54296     },
54297
54298     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54299         if(!noRefresh){
54300            this.refresh();
54301         }
54302         this.updateHeaderSortState();
54303     },
54304
54305     getScrollState : function(){
54306         
54307         var sb = this.scroller.dom;
54308         return {left: sb.scrollLeft, top: sb.scrollTop};
54309     },
54310
54311     stripeRows : function(startRow){
54312         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54313             return;
54314         }
54315         startRow = startRow || 0;
54316         var rows = this.getBodyTable().rows;
54317         var lrows = this.getLockedTable().rows;
54318         var cls = ' x-grid-row-alt ';
54319         for(var i = startRow, len = rows.length; i < len; i++){
54320             var row = rows[i], lrow = lrows[i];
54321             var isAlt = ((i+1) % 2 == 0);
54322             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54323             if(isAlt == hasAlt){
54324                 continue;
54325             }
54326             if(isAlt){
54327                 row.className += " x-grid-row-alt";
54328             }else{
54329                 row.className = row.className.replace("x-grid-row-alt", "");
54330             }
54331             if(lrow){
54332                 lrow.className = row.className;
54333             }
54334         }
54335     },
54336
54337     restoreScroll : function(state){
54338         //Roo.log('GridView.restoreScroll');
54339         var sb = this.scroller.dom;
54340         sb.scrollLeft = state.left;
54341         sb.scrollTop = state.top;
54342         this.syncScroll();
54343     },
54344
54345     syncScroll : function(){
54346         //Roo.log('GridView.syncScroll');
54347         var sb = this.scroller.dom;
54348         var sh = this.mainHd.dom;
54349         var bs = this.mainBody.dom;
54350         var lv = this.lockedBody.dom;
54351         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54352         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54353     },
54354
54355     handleScroll : function(e){
54356         this.syncScroll();
54357         var sb = this.scroller.dom;
54358         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54359         e.stopEvent();
54360     },
54361
54362     handleWheel : function(e){
54363         var d = e.getWheelDelta();
54364         this.scroller.dom.scrollTop -= d*22;
54365         // set this here to prevent jumpy scrolling on large tables
54366         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54367         e.stopEvent();
54368     },
54369
54370     renderRows : function(startRow, endRow){
54371         // pull in all the crap needed to render rows
54372         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54373         var colCount = cm.getColumnCount();
54374
54375         if(ds.getCount() < 1){
54376             return ["", ""];
54377         }
54378
54379         // build a map for all the columns
54380         var cs = [];
54381         for(var i = 0; i < colCount; i++){
54382             var name = cm.getDataIndex(i);
54383             cs[i] = {
54384                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54385                 renderer : cm.getRenderer(i),
54386                 id : cm.getColumnId(i),
54387                 locked : cm.isLocked(i),
54388                 has_editor : cm.isCellEditable(i)
54389             };
54390         }
54391
54392         startRow = startRow || 0;
54393         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54394
54395         // records to render
54396         var rs = ds.getRange(startRow, endRow);
54397
54398         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54399     },
54400
54401     // As much as I hate to duplicate code, this was branched because FireFox really hates
54402     // [].join("") on strings. The performance difference was substantial enough to
54403     // branch this function
54404     doRender : Roo.isGecko ?
54405             function(cs, rs, ds, startRow, colCount, stripe){
54406                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54407                 // buffers
54408                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54409                 
54410                 var hasListener = this.grid.hasListener('rowclass');
54411                 var rowcfg = {};
54412                 for(var j = 0, len = rs.length; j < len; j++){
54413                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54414                     for(var i = 0; i < colCount; i++){
54415                         c = cs[i];
54416                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54417                         p.id = c.id;
54418                         p.css = p.attr = "";
54419                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54420                         if(p.value == undefined || p.value === "") {
54421                             p.value = "&#160;";
54422                         }
54423                         if(c.has_editor){
54424                             p.css += ' x-grid-editable-cell';
54425                         }
54426                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
54427                             p.css +=  ' x-grid-dirty-cell';
54428                         }
54429                         var markup = ct.apply(p);
54430                         if(!c.locked){
54431                             cb+= markup;
54432                         }else{
54433                             lcb+= markup;
54434                         }
54435                     }
54436                     var alt = [];
54437                     if(stripe && ((rowIndex+1) % 2 == 0)){
54438                         alt.push("x-grid-row-alt")
54439                     }
54440                     if(r.dirty){
54441                         alt.push(  " x-grid-dirty-row");
54442                     }
54443                     rp.cells = lcb;
54444                     if(this.getRowClass){
54445                         alt.push(this.getRowClass(r, rowIndex));
54446                     }
54447                     if (hasListener) {
54448                         rowcfg = {
54449                              
54450                             record: r,
54451                             rowIndex : rowIndex,
54452                             rowClass : ''
54453                         };
54454                         this.grid.fireEvent('rowclass', this, rowcfg);
54455                         alt.push(rowcfg.rowClass);
54456                     }
54457                     rp.alt = alt.join(" ");
54458                     lbuf+= rt.apply(rp);
54459                     rp.cells = cb;
54460                     buf+=  rt.apply(rp);
54461                 }
54462                 return [lbuf, buf];
54463             } :
54464             function(cs, rs, ds, startRow, colCount, stripe){
54465                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54466                 // buffers
54467                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54468                 var hasListener = this.grid.hasListener('rowclass');
54469  
54470                 var rowcfg = {};
54471                 for(var j = 0, len = rs.length; j < len; j++){
54472                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54473                     for(var i = 0; i < colCount; i++){
54474                         c = cs[i];
54475                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54476                         p.id = c.id;
54477                         p.css = p.attr = "";
54478                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54479                         if(p.value == undefined || p.value === "") {
54480                             p.value = "&#160;";
54481                         }
54482                         //Roo.log(c);
54483                          if(c.has_editor){
54484                             p.css += ' x-grid-editable-cell';
54485                         }
54486                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54487                             p.css += ' x-grid-dirty-cell' 
54488                         }
54489                         
54490                         var markup = ct.apply(p);
54491                         if(!c.locked){
54492                             cb[cb.length] = markup;
54493                         }else{
54494                             lcb[lcb.length] = markup;
54495                         }
54496                     }
54497                     var alt = [];
54498                     if(stripe && ((rowIndex+1) % 2 == 0)){
54499                         alt.push( "x-grid-row-alt");
54500                     }
54501                     if(r.dirty){
54502                         alt.push(" x-grid-dirty-row");
54503                     }
54504                     rp.cells = lcb;
54505                     if(this.getRowClass){
54506                         alt.push( this.getRowClass(r, rowIndex));
54507                     }
54508                     if (hasListener) {
54509                         rowcfg = {
54510                              
54511                             record: r,
54512                             rowIndex : rowIndex,
54513                             rowClass : ''
54514                         };
54515                         this.grid.fireEvent('rowclass', this, rowcfg);
54516                         alt.push(rowcfg.rowClass);
54517                     }
54518                     
54519                     rp.alt = alt.join(" ");
54520                     rp.cells = lcb.join("");
54521                     lbuf[lbuf.length] = rt.apply(rp);
54522                     rp.cells = cb.join("");
54523                     buf[buf.length] =  rt.apply(rp);
54524                 }
54525                 return [lbuf.join(""), buf.join("")];
54526             },
54527
54528     renderBody : function(){
54529         var markup = this.renderRows();
54530         var bt = this.templates.body;
54531         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54532     },
54533
54534     /**
54535      * Refreshes the grid
54536      * @param {Boolean} headersToo
54537      */
54538     refresh : function(headersToo){
54539         this.fireEvent("beforerefresh", this);
54540         this.grid.stopEditing();
54541         var result = this.renderBody();
54542         this.lockedBody.update(result[0]);
54543         this.mainBody.update(result[1]);
54544         if(headersToo === true){
54545             this.updateHeaders();
54546             this.updateColumns();
54547             this.updateSplitters();
54548             this.updateHeaderSortState();
54549         }
54550         this.syncRowHeights();
54551         this.layout();
54552         this.fireEvent("refresh", this);
54553     },
54554
54555     handleColumnMove : function(cm, oldIndex, newIndex){
54556         this.indexMap = null;
54557         var s = this.getScrollState();
54558         this.refresh(true);
54559         this.restoreScroll(s);
54560         this.afterMove(newIndex);
54561     },
54562
54563     afterMove : function(colIndex){
54564         if(this.enableMoveAnim && Roo.enableFx){
54565             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54566         }
54567         // if multisort - fix sortOrder, and reload..
54568         if (this.grid.dataSource.multiSort) {
54569             // the we can call sort again..
54570             var dm = this.grid.dataSource;
54571             var cm = this.grid.colModel;
54572             var so = [];
54573             for(var i = 0; i < cm.config.length; i++ ) {
54574                 
54575                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54576                     continue; // dont' bother, it's not in sort list or being set.
54577                 }
54578                 
54579                 so.push(cm.config[i].dataIndex);
54580             };
54581             dm.sortOrder = so;
54582             dm.load(dm.lastOptions);
54583             
54584             
54585         }
54586         
54587     },
54588
54589     updateCell : function(dm, rowIndex, dataIndex){
54590         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54591         if(typeof colIndex == "undefined"){ // not present in grid
54592             return;
54593         }
54594         var cm = this.grid.colModel;
54595         var cell = this.getCell(rowIndex, colIndex);
54596         var cellText = this.getCellText(rowIndex, colIndex);
54597
54598         var p = {
54599             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54600             id : cm.getColumnId(colIndex),
54601             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54602         };
54603         var renderer = cm.getRenderer(colIndex);
54604         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54605         if(typeof val == "undefined" || val === "") {
54606             val = "&#160;";
54607         }
54608         cellText.innerHTML = val;
54609         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54610         this.syncRowHeights(rowIndex, rowIndex);
54611     },
54612
54613     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54614         var maxWidth = 0;
54615         if(this.grid.autoSizeHeaders){
54616             var h = this.getHeaderCellMeasure(colIndex);
54617             maxWidth = Math.max(maxWidth, h.scrollWidth);
54618         }
54619         var tb, index;
54620         if(this.cm.isLocked(colIndex)){
54621             tb = this.getLockedTable();
54622             index = colIndex;
54623         }else{
54624             tb = this.getBodyTable();
54625             index = colIndex - this.cm.getLockedCount();
54626         }
54627         if(tb && tb.rows){
54628             var rows = tb.rows;
54629             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54630             for(var i = 0; i < stopIndex; i++){
54631                 var cell = rows[i].childNodes[index].firstChild;
54632                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54633             }
54634         }
54635         return maxWidth + /*margin for error in IE*/ 5;
54636     },
54637     /**
54638      * Autofit a column to its content.
54639      * @param {Number} colIndex
54640      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54641      */
54642      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54643          if(this.cm.isHidden(colIndex)){
54644              return; // can't calc a hidden column
54645          }
54646         if(forceMinSize){
54647             var cid = this.cm.getColumnId(colIndex);
54648             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54649            if(this.grid.autoSizeHeaders){
54650                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54651            }
54652         }
54653         var newWidth = this.calcColumnWidth(colIndex);
54654         this.cm.setColumnWidth(colIndex,
54655             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54656         if(!suppressEvent){
54657             this.grid.fireEvent("columnresize", colIndex, newWidth);
54658         }
54659     },
54660
54661     /**
54662      * Autofits all columns to their content and then expands to fit any extra space in the grid
54663      */
54664      autoSizeColumns : function(){
54665         var cm = this.grid.colModel;
54666         var colCount = cm.getColumnCount();
54667         for(var i = 0; i < colCount; i++){
54668             this.autoSizeColumn(i, true, true);
54669         }
54670         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54671             this.fitColumns();
54672         }else{
54673             this.updateColumns();
54674             this.layout();
54675         }
54676     },
54677
54678     /**
54679      * Autofits all columns to the grid's width proportionate with their current size
54680      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54681      */
54682     fitColumns : function(reserveScrollSpace){
54683         var cm = this.grid.colModel;
54684         var colCount = cm.getColumnCount();
54685         var cols = [];
54686         var width = 0;
54687         var i, w;
54688         for (i = 0; i < colCount; i++){
54689             if(!cm.isHidden(i) && !cm.isFixed(i)){
54690                 w = cm.getColumnWidth(i);
54691                 cols.push(i);
54692                 cols.push(w);
54693                 width += w;
54694             }
54695         }
54696         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54697         if(reserveScrollSpace){
54698             avail -= 17;
54699         }
54700         var frac = (avail - cm.getTotalWidth())/width;
54701         while (cols.length){
54702             w = cols.pop();
54703             i = cols.pop();
54704             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54705         }
54706         this.updateColumns();
54707         this.layout();
54708     },
54709
54710     onRowSelect : function(rowIndex){
54711         var row = this.getRowComposite(rowIndex);
54712         row.addClass("x-grid-row-selected");
54713     },
54714
54715     onRowDeselect : function(rowIndex){
54716         var row = this.getRowComposite(rowIndex);
54717         row.removeClass("x-grid-row-selected");
54718     },
54719
54720     onCellSelect : function(row, col){
54721         var cell = this.getCell(row, col);
54722         if(cell){
54723             Roo.fly(cell).addClass("x-grid-cell-selected");
54724         }
54725     },
54726
54727     onCellDeselect : function(row, col){
54728         var cell = this.getCell(row, col);
54729         if(cell){
54730             Roo.fly(cell).removeClass("x-grid-cell-selected");
54731         }
54732     },
54733
54734     updateHeaderSortState : function(){
54735         
54736         // sort state can be single { field: xxx, direction : yyy}
54737         // or   { xxx=>ASC , yyy : DESC ..... }
54738         
54739         var mstate = {};
54740         if (!this.ds.multiSort) { 
54741             var state = this.ds.getSortState();
54742             if(!state){
54743                 return;
54744             }
54745             mstate[state.field] = state.direction;
54746             // FIXME... - this is not used here.. but might be elsewhere..
54747             this.sortState = state;
54748             
54749         } else {
54750             mstate = this.ds.sortToggle;
54751         }
54752         //remove existing sort classes..
54753         
54754         var sc = this.sortClasses;
54755         var hds = this.el.select(this.headerSelector).removeClass(sc);
54756         
54757         for(var f in mstate) {
54758         
54759             var sortColumn = this.cm.findColumnIndex(f);
54760             
54761             if(sortColumn != -1){
54762                 var sortDir = mstate[f];        
54763                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54764             }
54765         }
54766         
54767          
54768         
54769     },
54770
54771
54772     handleHeaderClick : function(g, index,e){
54773         
54774         Roo.log("header click");
54775         
54776         if (Roo.isTouch) {
54777             // touch events on header are handled by context
54778             this.handleHdCtx(g,index,e);
54779             return;
54780         }
54781         
54782         
54783         if(this.headersDisabled){
54784             return;
54785         }
54786         var dm = g.dataSource, cm = g.colModel;
54787         if(!cm.isSortable(index)){
54788             return;
54789         }
54790         g.stopEditing();
54791         
54792         if (dm.multiSort) {
54793             // update the sortOrder
54794             var so = [];
54795             for(var i = 0; i < cm.config.length; i++ ) {
54796                 
54797                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54798                     continue; // dont' bother, it's not in sort list or being set.
54799                 }
54800                 
54801                 so.push(cm.config[i].dataIndex);
54802             };
54803             dm.sortOrder = so;
54804         }
54805         
54806         
54807         dm.sort(cm.getDataIndex(index));
54808     },
54809
54810
54811     destroy : function(){
54812         if(this.colMenu){
54813             this.colMenu.removeAll();
54814             Roo.menu.MenuMgr.unregister(this.colMenu);
54815             this.colMenu.getEl().remove();
54816             delete this.colMenu;
54817         }
54818         if(this.hmenu){
54819             this.hmenu.removeAll();
54820             Roo.menu.MenuMgr.unregister(this.hmenu);
54821             this.hmenu.getEl().remove();
54822             delete this.hmenu;
54823         }
54824         if(this.grid.enableColumnMove){
54825             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54826             if(dds){
54827                 for(var dd in dds){
54828                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54829                         var elid = dds[dd].dragElId;
54830                         dds[dd].unreg();
54831                         Roo.get(elid).remove();
54832                     } else if(dds[dd].config.isTarget){
54833                         dds[dd].proxyTop.remove();
54834                         dds[dd].proxyBottom.remove();
54835                         dds[dd].unreg();
54836                     }
54837                     if(Roo.dd.DDM.locationCache[dd]){
54838                         delete Roo.dd.DDM.locationCache[dd];
54839                     }
54840                 }
54841                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54842             }
54843         }
54844         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54845         this.bind(null, null);
54846         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54847     },
54848
54849     handleLockChange : function(){
54850         this.refresh(true);
54851     },
54852
54853     onDenyColumnLock : function(){
54854
54855     },
54856
54857     onDenyColumnHide : function(){
54858
54859     },
54860
54861     handleHdMenuClick : function(item){
54862         var index = this.hdCtxIndex;
54863         var cm = this.cm, ds = this.ds;
54864         switch(item.id){
54865             case "asc":
54866                 ds.sort(cm.getDataIndex(index), "ASC");
54867                 break;
54868             case "desc":
54869                 ds.sort(cm.getDataIndex(index), "DESC");
54870                 break;
54871             case "lock":
54872                 var lc = cm.getLockedCount();
54873                 if(cm.getColumnCount(true) <= lc+1){
54874                     this.onDenyColumnLock();
54875                     return;
54876                 }
54877                 if(lc != index){
54878                     cm.setLocked(index, true, true);
54879                     cm.moveColumn(index, lc);
54880                     this.grid.fireEvent("columnmove", index, lc);
54881                 }else{
54882                     cm.setLocked(index, true);
54883                 }
54884             break;
54885             case "unlock":
54886                 var lc = cm.getLockedCount();
54887                 if((lc-1) != index){
54888                     cm.setLocked(index, false, true);
54889                     cm.moveColumn(index, lc-1);
54890                     this.grid.fireEvent("columnmove", index, lc-1);
54891                 }else{
54892                     cm.setLocked(index, false);
54893                 }
54894             break;
54895             case 'wider': // used to expand cols on touch..
54896             case 'narrow':
54897                 var cw = cm.getColumnWidth(index);
54898                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54899                 cw = Math.max(0, cw);
54900                 cw = Math.min(cw,4000);
54901                 cm.setColumnWidth(index, cw);
54902                 break;
54903                 
54904             default:
54905                 index = cm.getIndexById(item.id.substr(4));
54906                 if(index != -1){
54907                     if(item.checked && cm.getColumnCount(true) <= 1){
54908                         this.onDenyColumnHide();
54909                         return false;
54910                     }
54911                     cm.setHidden(index, item.checked);
54912                 }
54913         }
54914         return true;
54915     },
54916
54917     beforeColMenuShow : function(){
54918         var cm = this.cm,  colCount = cm.getColumnCount();
54919         this.colMenu.removeAll();
54920         for(var i = 0; i < colCount; i++){
54921             this.colMenu.add(new Roo.menu.CheckItem({
54922                 id: "col-"+cm.getColumnId(i),
54923                 text: cm.getColumnHeader(i),
54924                 checked: !cm.isHidden(i),
54925                 hideOnClick:false
54926             }));
54927         }
54928     },
54929
54930     handleHdCtx : function(g, index, e){
54931         e.stopEvent();
54932         var hd = this.getHeaderCell(index);
54933         this.hdCtxIndex = index;
54934         var ms = this.hmenu.items, cm = this.cm;
54935         ms.get("asc").setDisabled(!cm.isSortable(index));
54936         ms.get("desc").setDisabled(!cm.isSortable(index));
54937         if(this.grid.enableColLock !== false){
54938             ms.get("lock").setDisabled(cm.isLocked(index));
54939             ms.get("unlock").setDisabled(!cm.isLocked(index));
54940         }
54941         this.hmenu.show(hd, "tl-bl");
54942     },
54943
54944     handleHdOver : function(e){
54945         var hd = this.findHeaderCell(e.getTarget());
54946         if(hd && !this.headersDisabled){
54947             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54948                this.fly(hd).addClass("x-grid-hd-over");
54949             }
54950         }
54951     },
54952
54953     handleHdOut : function(e){
54954         var hd = this.findHeaderCell(e.getTarget());
54955         if(hd){
54956             this.fly(hd).removeClass("x-grid-hd-over");
54957         }
54958     },
54959
54960     handleSplitDblClick : function(e, t){
54961         var i = this.getCellIndex(t);
54962         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54963             this.autoSizeColumn(i, true);
54964             this.layout();
54965         }
54966     },
54967
54968     render : function(){
54969
54970         var cm = this.cm;
54971         var colCount = cm.getColumnCount();
54972
54973         if(this.grid.monitorWindowResize === true){
54974             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54975         }
54976         var header = this.renderHeaders();
54977         var body = this.templates.body.apply({rows:""});
54978         var html = this.templates.master.apply({
54979             lockedBody: body,
54980             body: body,
54981             lockedHeader: header[0],
54982             header: header[1]
54983         });
54984
54985         //this.updateColumns();
54986
54987         this.grid.getGridEl().dom.innerHTML = html;
54988
54989         this.initElements();
54990         
54991         // a kludge to fix the random scolling effect in webkit
54992         this.el.on("scroll", function() {
54993             this.el.dom.scrollTop=0; // hopefully not recursive..
54994         },this);
54995
54996         this.scroller.on("scroll", this.handleScroll, this);
54997         this.lockedBody.on("mousewheel", this.handleWheel, this);
54998         this.mainBody.on("mousewheel", this.handleWheel, this);
54999
55000         this.mainHd.on("mouseover", this.handleHdOver, this);
55001         this.mainHd.on("mouseout", this.handleHdOut, this);
55002         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
55003                 {delegate: "."+this.splitClass});
55004
55005         this.lockedHd.on("mouseover", this.handleHdOver, this);
55006         this.lockedHd.on("mouseout", this.handleHdOut, this);
55007         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
55008                 {delegate: "."+this.splitClass});
55009
55010         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
55011             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55012         }
55013
55014         this.updateSplitters();
55015
55016         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
55017             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55018             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55019         }
55020
55021         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
55022             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
55023             this.hmenu.add(
55024                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
55025                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
55026             );
55027             if(this.grid.enableColLock !== false){
55028                 this.hmenu.add('-',
55029                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
55030                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
55031                 );
55032             }
55033             if (Roo.isTouch) {
55034                  this.hmenu.add('-',
55035                     {id:"wider", text: this.columnsWiderText},
55036                     {id:"narrow", text: this.columnsNarrowText }
55037                 );
55038                 
55039                  
55040             }
55041             
55042             if(this.grid.enableColumnHide !== false){
55043
55044                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
55045                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
55046                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
55047
55048                 this.hmenu.add('-',
55049                     {id:"columns", text: this.columnsText, menu: this.colMenu}
55050                 );
55051             }
55052             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
55053
55054             this.grid.on("headercontextmenu", this.handleHdCtx, this);
55055         }
55056
55057         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
55058             this.dd = new Roo.grid.GridDragZone(this.grid, {
55059                 ddGroup : this.grid.ddGroup || 'GridDD'
55060             });
55061             
55062         }
55063
55064         /*
55065         for(var i = 0; i < colCount; i++){
55066             if(cm.isHidden(i)){
55067                 this.hideColumn(i);
55068             }
55069             if(cm.config[i].align){
55070                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
55071                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
55072             }
55073         }*/
55074         
55075         this.updateHeaderSortState();
55076
55077         this.beforeInitialResize();
55078         this.layout(true);
55079
55080         // two part rendering gives faster view to the user
55081         this.renderPhase2.defer(1, this);
55082     },
55083
55084     renderPhase2 : function(){
55085         // render the rows now
55086         this.refresh();
55087         if(this.grid.autoSizeColumns){
55088             this.autoSizeColumns();
55089         }
55090     },
55091
55092     beforeInitialResize : function(){
55093
55094     },
55095
55096     onColumnSplitterMoved : function(i, w){
55097         this.userResized = true;
55098         var cm = this.grid.colModel;
55099         cm.setColumnWidth(i, w, true);
55100         var cid = cm.getColumnId(i);
55101         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55102         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55103         this.updateSplitters();
55104         this.layout();
55105         this.grid.fireEvent("columnresize", i, w);
55106     },
55107
55108     syncRowHeights : function(startIndex, endIndex){
55109         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
55110             startIndex = startIndex || 0;
55111             var mrows = this.getBodyTable().rows;
55112             var lrows = this.getLockedTable().rows;
55113             var len = mrows.length-1;
55114             endIndex = Math.min(endIndex || len, len);
55115             for(var i = startIndex; i <= endIndex; i++){
55116                 var m = mrows[i], l = lrows[i];
55117                 var h = Math.max(m.offsetHeight, l.offsetHeight);
55118                 m.style.height = l.style.height = h + "px";
55119             }
55120         }
55121     },
55122
55123     layout : function(initialRender, is2ndPass){
55124         var g = this.grid;
55125         var auto = g.autoHeight;
55126         var scrollOffset = 16;
55127         var c = g.getGridEl(), cm = this.cm,
55128                 expandCol = g.autoExpandColumn,
55129                 gv = this;
55130         //c.beginMeasure();
55131
55132         if(!c.dom.offsetWidth){ // display:none?
55133             if(initialRender){
55134                 this.lockedWrap.show();
55135                 this.mainWrap.show();
55136             }
55137             return;
55138         }
55139
55140         var hasLock = this.cm.isLocked(0);
55141
55142         var tbh = this.headerPanel.getHeight();
55143         var bbh = this.footerPanel.getHeight();
55144
55145         if(auto){
55146             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
55147             var newHeight = ch + c.getBorderWidth("tb");
55148             if(g.maxHeight){
55149                 newHeight = Math.min(g.maxHeight, newHeight);
55150             }
55151             c.setHeight(newHeight);
55152         }
55153
55154         if(g.autoWidth){
55155             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
55156         }
55157
55158         var s = this.scroller;
55159
55160         var csize = c.getSize(true);
55161
55162         this.el.setSize(csize.width, csize.height);
55163
55164         this.headerPanel.setWidth(csize.width);
55165         this.footerPanel.setWidth(csize.width);
55166
55167         var hdHeight = this.mainHd.getHeight();
55168         var vw = csize.width;
55169         var vh = csize.height - (tbh + bbh);
55170
55171         s.setSize(vw, vh);
55172
55173         var bt = this.getBodyTable();
55174         
55175         if(cm.getLockedCount() == cm.config.length){
55176             bt = this.getLockedTable();
55177         }
55178         
55179         var ltWidth = hasLock ?
55180                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55181
55182         var scrollHeight = bt.offsetHeight;
55183         var scrollWidth = ltWidth + bt.offsetWidth;
55184         var vscroll = false, hscroll = false;
55185
55186         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55187
55188         var lw = this.lockedWrap, mw = this.mainWrap;
55189         var lb = this.lockedBody, mb = this.mainBody;
55190
55191         setTimeout(function(){
55192             var t = s.dom.offsetTop;
55193             var w = s.dom.clientWidth,
55194                 h = s.dom.clientHeight;
55195
55196             lw.setTop(t);
55197             lw.setSize(ltWidth, h);
55198
55199             mw.setLeftTop(ltWidth, t);
55200             mw.setSize(w-ltWidth, h);
55201
55202             lb.setHeight(h-hdHeight);
55203             mb.setHeight(h-hdHeight);
55204
55205             if(is2ndPass !== true && !gv.userResized && expandCol){
55206                 // high speed resize without full column calculation
55207                 
55208                 var ci = cm.getIndexById(expandCol);
55209                 if (ci < 0) {
55210                     ci = cm.findColumnIndex(expandCol);
55211                 }
55212                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55213                 var expandId = cm.getColumnId(ci);
55214                 var  tw = cm.getTotalWidth(false);
55215                 var currentWidth = cm.getColumnWidth(ci);
55216                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55217                 if(currentWidth != cw){
55218                     cm.setColumnWidth(ci, cw, true);
55219                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55220                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55221                     gv.updateSplitters();
55222                     gv.layout(false, true);
55223                 }
55224             }
55225
55226             if(initialRender){
55227                 lw.show();
55228                 mw.show();
55229             }
55230             //c.endMeasure();
55231         }, 10);
55232     },
55233
55234     onWindowResize : function(){
55235         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55236             return;
55237         }
55238         this.layout();
55239     },
55240
55241     appendFooter : function(parentEl){
55242         return null;
55243     },
55244
55245     sortAscText : "Sort Ascending",
55246     sortDescText : "Sort Descending",
55247     lockText : "Lock Column",
55248     unlockText : "Unlock Column",
55249     columnsText : "Columns",
55250  
55251     columnsWiderText : "Wider",
55252     columnsNarrowText : "Thinner"
55253 });
55254
55255
55256 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55257     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55258     this.proxy.el.addClass('x-grid3-col-dd');
55259 };
55260
55261 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55262     handleMouseDown : function(e){
55263
55264     },
55265
55266     callHandleMouseDown : function(e){
55267         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55268     }
55269 });
55270 /*
55271  * Based on:
55272  * Ext JS Library 1.1.1
55273  * Copyright(c) 2006-2007, Ext JS, LLC.
55274  *
55275  * Originally Released Under LGPL - original licence link has changed is not relivant.
55276  *
55277  * Fork - LGPL
55278  * <script type="text/javascript">
55279  */
55280  
55281 // private
55282 // This is a support class used internally by the Grid components
55283 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55284     this.grid = grid;
55285     this.view = grid.getView();
55286     this.proxy = this.view.resizeProxy;
55287     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55288         "gridSplitters" + this.grid.getGridEl().id, {
55289         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55290     });
55291     this.setHandleElId(Roo.id(hd));
55292     this.setOuterHandleElId(Roo.id(hd2));
55293     this.scroll = false;
55294 };
55295 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55296     fly: Roo.Element.fly,
55297
55298     b4StartDrag : function(x, y){
55299         this.view.headersDisabled = true;
55300         this.proxy.setHeight(this.view.mainWrap.getHeight());
55301         var w = this.cm.getColumnWidth(this.cellIndex);
55302         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55303         this.resetConstraints();
55304         this.setXConstraint(minw, 1000);
55305         this.setYConstraint(0, 0);
55306         this.minX = x - minw;
55307         this.maxX = x + 1000;
55308         this.startPos = x;
55309         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55310     },
55311
55312
55313     handleMouseDown : function(e){
55314         ev = Roo.EventObject.setEvent(e);
55315         var t = this.fly(ev.getTarget());
55316         if(t.hasClass("x-grid-split")){
55317             this.cellIndex = this.view.getCellIndex(t.dom);
55318             this.split = t.dom;
55319             this.cm = this.grid.colModel;
55320             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55321                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55322             }
55323         }
55324     },
55325
55326     endDrag : function(e){
55327         this.view.headersDisabled = false;
55328         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55329         var diff = endX - this.startPos;
55330         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55331     },
55332
55333     autoOffset : function(){
55334         this.setDelta(0,0);
55335     }
55336 });/*
55337  * Based on:
55338  * Ext JS Library 1.1.1
55339  * Copyright(c) 2006-2007, Ext JS, LLC.
55340  *
55341  * Originally Released Under LGPL - original licence link has changed is not relivant.
55342  *
55343  * Fork - LGPL
55344  * <script type="text/javascript">
55345  */
55346  
55347 // private
55348 // This is a support class used internally by the Grid components
55349 Roo.grid.GridDragZone = function(grid, config){
55350     this.view = grid.getView();
55351     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55352     if(this.view.lockedBody){
55353         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55354         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55355     }
55356     this.scroll = false;
55357     this.grid = grid;
55358     this.ddel = document.createElement('div');
55359     this.ddel.className = 'x-grid-dd-wrap';
55360 };
55361
55362 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55363     ddGroup : "GridDD",
55364
55365     getDragData : function(e){
55366         var t = Roo.lib.Event.getTarget(e);
55367         var rowIndex = this.view.findRowIndex(t);
55368         var sm = this.grid.selModel;
55369             
55370         //Roo.log(rowIndex);
55371         
55372         if (sm.getSelectedCell) {
55373             // cell selection..
55374             if (!sm.getSelectedCell()) {
55375                 return false;
55376             }
55377             if (rowIndex != sm.getSelectedCell()[0]) {
55378                 return false;
55379             }
55380         
55381         }
55382         
55383         if(rowIndex !== false){
55384             
55385             // if editorgrid.. 
55386             
55387             
55388             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55389                
55390             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55391               //  
55392             //}
55393             if (e.hasModifier()){
55394                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55395             }
55396             
55397             Roo.log("getDragData");
55398             
55399             return {
55400                 grid: this.grid,
55401                 ddel: this.ddel,
55402                 rowIndex: rowIndex,
55403                 selections:sm.getSelections ? sm.getSelections() : (
55404                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55405                 )
55406             };
55407         }
55408         return false;
55409     },
55410
55411     onInitDrag : function(e){
55412         var data = this.dragData;
55413         this.ddel.innerHTML = this.grid.getDragDropText();
55414         this.proxy.update(this.ddel);
55415         // fire start drag?
55416     },
55417
55418     afterRepair : function(){
55419         this.dragging = false;
55420     },
55421
55422     getRepairXY : function(e, data){
55423         return false;
55424     },
55425
55426     onEndDrag : function(data, e){
55427         // fire end drag?
55428     },
55429
55430     onValidDrop : function(dd, e, id){
55431         // fire drag drop?
55432         this.hideProxy();
55433     },
55434
55435     beforeInvalidDrop : function(e, id){
55436
55437     }
55438 });/*
55439  * Based on:
55440  * Ext JS Library 1.1.1
55441  * Copyright(c) 2006-2007, Ext JS, LLC.
55442  *
55443  * Originally Released Under LGPL - original licence link has changed is not relivant.
55444  *
55445  * Fork - LGPL
55446  * <script type="text/javascript">
55447  */
55448  
55449
55450 /**
55451  * @class Roo.grid.ColumnModel
55452  * @extends Roo.util.Observable
55453  * This is the default implementation of a ColumnModel used by the Grid. It defines
55454  * the columns in the grid.
55455  * <br>Usage:<br>
55456  <pre><code>
55457  var colModel = new Roo.grid.ColumnModel([
55458         {header: "Ticker", width: 60, sortable: true, locked: true},
55459         {header: "Company Name", width: 150, sortable: true},
55460         {header: "Market Cap.", width: 100, sortable: true},
55461         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55462         {header: "Employees", width: 100, sortable: true, resizable: false}
55463  ]);
55464  </code></pre>
55465  * <p>
55466  
55467  * The config options listed for this class are options which may appear in each
55468  * individual column definition.
55469  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55470  * @constructor
55471  * @param {Object} config An Array of column config objects. See this class's
55472  * config objects for details.
55473 */
55474 Roo.grid.ColumnModel = function(config){
55475         /**
55476      * The config passed into the constructor
55477      */
55478     this.config = config;
55479     this.lookup = {};
55480
55481     // if no id, create one
55482     // if the column does not have a dataIndex mapping,
55483     // map it to the order it is in the config
55484     for(var i = 0, len = config.length; i < len; i++){
55485         var c = config[i];
55486         if(typeof c.dataIndex == "undefined"){
55487             c.dataIndex = i;
55488         }
55489         if(typeof c.renderer == "string"){
55490             c.renderer = Roo.util.Format[c.renderer];
55491         }
55492         if(typeof c.id == "undefined"){
55493             c.id = Roo.id();
55494         }
55495         if(c.editor && c.editor.xtype){
55496             c.editor  = Roo.factory(c.editor, Roo.grid);
55497         }
55498         if(c.editor && c.editor.isFormField){
55499             c.editor = new Roo.grid.GridEditor(c.editor);
55500         }
55501         this.lookup[c.id] = c;
55502     }
55503
55504     /**
55505      * The width of columns which have no width specified (defaults to 100)
55506      * @type Number
55507      */
55508     this.defaultWidth = 100;
55509
55510     /**
55511      * Default sortable of columns which have no sortable specified (defaults to false)
55512      * @type Boolean
55513      */
55514     this.defaultSortable = false;
55515
55516     this.addEvents({
55517         /**
55518              * @event widthchange
55519              * Fires when the width of a column changes.
55520              * @param {ColumnModel} this
55521              * @param {Number} columnIndex The column index
55522              * @param {Number} newWidth The new width
55523              */
55524             "widthchange": true,
55525         /**
55526              * @event headerchange
55527              * Fires when the text of a header changes.
55528              * @param {ColumnModel} this
55529              * @param {Number} columnIndex The column index
55530              * @param {Number} newText The new header text
55531              */
55532             "headerchange": true,
55533         /**
55534              * @event hiddenchange
55535              * Fires when a column is hidden or "unhidden".
55536              * @param {ColumnModel} this
55537              * @param {Number} columnIndex The column index
55538              * @param {Boolean} hidden true if hidden, false otherwise
55539              */
55540             "hiddenchange": true,
55541             /**
55542          * @event columnmoved
55543          * Fires when a column is moved.
55544          * @param {ColumnModel} this
55545          * @param {Number} oldIndex
55546          * @param {Number} newIndex
55547          */
55548         "columnmoved" : true,
55549         /**
55550          * @event columlockchange
55551          * Fires when a column's locked state is changed
55552          * @param {ColumnModel} this
55553          * @param {Number} colIndex
55554          * @param {Boolean} locked true if locked
55555          */
55556         "columnlockchange" : true
55557     });
55558     Roo.grid.ColumnModel.superclass.constructor.call(this);
55559 };
55560 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55561     /**
55562      * @cfg {String} header The header text to display in the Grid view.
55563      */
55564     /**
55565      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55566      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55567      * specified, the column's index is used as an index into the Record's data Array.
55568      */
55569     /**
55570      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55571      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55572      */
55573     /**
55574      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55575      * Defaults to the value of the {@link #defaultSortable} property.
55576      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55577      */
55578     /**
55579      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55580      */
55581     /**
55582      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55583      */
55584     /**
55585      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55586      */
55587     /**
55588      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55589      */
55590     /**
55591      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55592      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55593      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55594      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55595      */
55596        /**
55597      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55598      */
55599     /**
55600      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55601      */
55602     /**
55603      * @cfg {String} cursor (Optional)
55604      */
55605     /**
55606      * @cfg {String} tooltip (Optional)
55607      */
55608     /**
55609      * @cfg {Number} xs (Optional)
55610      */
55611     /**
55612      * @cfg {Number} sm (Optional)
55613      */
55614     /**
55615      * @cfg {Number} md (Optional)
55616      */
55617     /**
55618      * @cfg {Number} lg (Optional)
55619      */
55620     /**
55621      * Returns the id of the column at the specified index.
55622      * @param {Number} index The column index
55623      * @return {String} the id
55624      */
55625     getColumnId : function(index){
55626         return this.config[index].id;
55627     },
55628
55629     /**
55630      * Returns the column for a specified id.
55631      * @param {String} id The column id
55632      * @return {Object} the column
55633      */
55634     getColumnById : function(id){
55635         return this.lookup[id];
55636     },
55637
55638     
55639     /**
55640      * Returns the column for a specified dataIndex.
55641      * @param {String} dataIndex The column dataIndex
55642      * @return {Object|Boolean} the column or false if not found
55643      */
55644     getColumnByDataIndex: function(dataIndex){
55645         var index = this.findColumnIndex(dataIndex);
55646         return index > -1 ? this.config[index] : false;
55647     },
55648     
55649     /**
55650      * Returns the index for a specified column id.
55651      * @param {String} id The column id
55652      * @return {Number} the index, or -1 if not found
55653      */
55654     getIndexById : function(id){
55655         for(var i = 0, len = this.config.length; i < len; i++){
55656             if(this.config[i].id == id){
55657                 return i;
55658             }
55659         }
55660         return -1;
55661     },
55662     
55663     /**
55664      * Returns the index for a specified column dataIndex.
55665      * @param {String} dataIndex The column dataIndex
55666      * @return {Number} the index, or -1 if not found
55667      */
55668     
55669     findColumnIndex : function(dataIndex){
55670         for(var i = 0, len = this.config.length; i < len; i++){
55671             if(this.config[i].dataIndex == dataIndex){
55672                 return i;
55673             }
55674         }
55675         return -1;
55676     },
55677     
55678     
55679     moveColumn : function(oldIndex, newIndex){
55680         var c = this.config[oldIndex];
55681         this.config.splice(oldIndex, 1);
55682         this.config.splice(newIndex, 0, c);
55683         this.dataMap = null;
55684         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55685     },
55686
55687     isLocked : function(colIndex){
55688         return this.config[colIndex].locked === true;
55689     },
55690
55691     setLocked : function(colIndex, value, suppressEvent){
55692         if(this.isLocked(colIndex) == value){
55693             return;
55694         }
55695         this.config[colIndex].locked = value;
55696         if(!suppressEvent){
55697             this.fireEvent("columnlockchange", this, colIndex, value);
55698         }
55699     },
55700
55701     getTotalLockedWidth : function(){
55702         var totalWidth = 0;
55703         for(var i = 0; i < this.config.length; i++){
55704             if(this.isLocked(i) && !this.isHidden(i)){
55705                 this.totalWidth += this.getColumnWidth(i);
55706             }
55707         }
55708         return totalWidth;
55709     },
55710
55711     getLockedCount : function(){
55712         for(var i = 0, len = this.config.length; i < len; i++){
55713             if(!this.isLocked(i)){
55714                 return i;
55715             }
55716         }
55717         
55718         return this.config.length;
55719     },
55720
55721     /**
55722      * Returns the number of columns.
55723      * @return {Number}
55724      */
55725     getColumnCount : function(visibleOnly){
55726         if(visibleOnly === true){
55727             var c = 0;
55728             for(var i = 0, len = this.config.length; i < len; i++){
55729                 if(!this.isHidden(i)){
55730                     c++;
55731                 }
55732             }
55733             return c;
55734         }
55735         return this.config.length;
55736     },
55737
55738     /**
55739      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55740      * @param {Function} fn
55741      * @param {Object} scope (optional)
55742      * @return {Array} result
55743      */
55744     getColumnsBy : function(fn, scope){
55745         var r = [];
55746         for(var i = 0, len = this.config.length; i < len; i++){
55747             var c = this.config[i];
55748             if(fn.call(scope||this, c, i) === true){
55749                 r[r.length] = c;
55750             }
55751         }
55752         return r;
55753     },
55754
55755     /**
55756      * Returns true if the specified column is sortable.
55757      * @param {Number} col The column index
55758      * @return {Boolean}
55759      */
55760     isSortable : function(col){
55761         if(typeof this.config[col].sortable == "undefined"){
55762             return this.defaultSortable;
55763         }
55764         return this.config[col].sortable;
55765     },
55766
55767     /**
55768      * Returns the rendering (formatting) function defined for the column.
55769      * @param {Number} col The column index.
55770      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55771      */
55772     getRenderer : function(col){
55773         if(!this.config[col].renderer){
55774             return Roo.grid.ColumnModel.defaultRenderer;
55775         }
55776         return this.config[col].renderer;
55777     },
55778
55779     /**
55780      * Sets the rendering (formatting) function for a column.
55781      * @param {Number} col The column index
55782      * @param {Function} fn The function to use to process the cell's raw data
55783      * to return HTML markup for the grid view. The render function is called with
55784      * the following parameters:<ul>
55785      * <li>Data value.</li>
55786      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55787      * <li>css A CSS style string to apply to the table cell.</li>
55788      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55789      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55790      * <li>Row index</li>
55791      * <li>Column index</li>
55792      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55793      */
55794     setRenderer : function(col, fn){
55795         this.config[col].renderer = fn;
55796     },
55797
55798     /**
55799      * Returns the width for the specified column.
55800      * @param {Number} col The column index
55801      * @return {Number}
55802      */
55803     getColumnWidth : function(col){
55804         return this.config[col].width * 1 || this.defaultWidth;
55805     },
55806
55807     /**
55808      * Sets the width for a column.
55809      * @param {Number} col The column index
55810      * @param {Number} width The new width
55811      */
55812     setColumnWidth : function(col, width, suppressEvent){
55813         this.config[col].width = width;
55814         this.totalWidth = null;
55815         if(!suppressEvent){
55816              this.fireEvent("widthchange", this, col, width);
55817         }
55818     },
55819
55820     /**
55821      * Returns the total width of all columns.
55822      * @param {Boolean} includeHidden True to include hidden column widths
55823      * @return {Number}
55824      */
55825     getTotalWidth : function(includeHidden){
55826         if(!this.totalWidth){
55827             this.totalWidth = 0;
55828             for(var i = 0, len = this.config.length; i < len; i++){
55829                 if(includeHidden || !this.isHidden(i)){
55830                     this.totalWidth += this.getColumnWidth(i);
55831                 }
55832             }
55833         }
55834         return this.totalWidth;
55835     },
55836
55837     /**
55838      * Returns the header for the specified column.
55839      * @param {Number} col The column index
55840      * @return {String}
55841      */
55842     getColumnHeader : function(col){
55843         return this.config[col].header;
55844     },
55845
55846     /**
55847      * Sets the header for a column.
55848      * @param {Number} col The column index
55849      * @param {String} header The new header
55850      */
55851     setColumnHeader : function(col, header){
55852         this.config[col].header = header;
55853         this.fireEvent("headerchange", this, col, header);
55854     },
55855
55856     /**
55857      * Returns the tooltip for the specified column.
55858      * @param {Number} col The column index
55859      * @return {String}
55860      */
55861     getColumnTooltip : function(col){
55862             return this.config[col].tooltip;
55863     },
55864     /**
55865      * Sets the tooltip for a column.
55866      * @param {Number} col The column index
55867      * @param {String} tooltip The new tooltip
55868      */
55869     setColumnTooltip : function(col, tooltip){
55870             this.config[col].tooltip = tooltip;
55871     },
55872
55873     /**
55874      * Returns the dataIndex for the specified column.
55875      * @param {Number} col The column index
55876      * @return {Number}
55877      */
55878     getDataIndex : function(col){
55879         return this.config[col].dataIndex;
55880     },
55881
55882     /**
55883      * Sets the dataIndex for a column.
55884      * @param {Number} col The column index
55885      * @param {Number} dataIndex The new dataIndex
55886      */
55887     setDataIndex : function(col, dataIndex){
55888         this.config[col].dataIndex = dataIndex;
55889     },
55890
55891     
55892     
55893     /**
55894      * Returns true if the cell is editable.
55895      * @param {Number} colIndex The column index
55896      * @param {Number} rowIndex The row index - this is nto actually used..?
55897      * @return {Boolean}
55898      */
55899     isCellEditable : function(colIndex, rowIndex){
55900         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55901     },
55902
55903     /**
55904      * Returns the editor defined for the cell/column.
55905      * return false or null to disable editing.
55906      * @param {Number} colIndex The column index
55907      * @param {Number} rowIndex The row index
55908      * @return {Object}
55909      */
55910     getCellEditor : function(colIndex, rowIndex){
55911         return this.config[colIndex].editor;
55912     },
55913
55914     /**
55915      * Sets if a column is editable.
55916      * @param {Number} col The column index
55917      * @param {Boolean} editable True if the column is editable
55918      */
55919     setEditable : function(col, editable){
55920         this.config[col].editable = editable;
55921     },
55922
55923
55924     /**
55925      * Returns true if the column is hidden.
55926      * @param {Number} colIndex The column index
55927      * @return {Boolean}
55928      */
55929     isHidden : function(colIndex){
55930         return this.config[colIndex].hidden;
55931     },
55932
55933
55934     /**
55935      * Returns true if the column width cannot be changed
55936      */
55937     isFixed : function(colIndex){
55938         return this.config[colIndex].fixed;
55939     },
55940
55941     /**
55942      * Returns true if the column can be resized
55943      * @return {Boolean}
55944      */
55945     isResizable : function(colIndex){
55946         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55947     },
55948     /**
55949      * Sets if a column is hidden.
55950      * @param {Number} colIndex The column index
55951      * @param {Boolean} hidden True if the column is hidden
55952      */
55953     setHidden : function(colIndex, hidden){
55954         this.config[colIndex].hidden = hidden;
55955         this.totalWidth = null;
55956         this.fireEvent("hiddenchange", this, colIndex, hidden);
55957     },
55958
55959     /**
55960      * Sets the editor for a column.
55961      * @param {Number} col The column index
55962      * @param {Object} editor The editor object
55963      */
55964     setEditor : function(col, editor){
55965         this.config[col].editor = editor;
55966     }
55967 });
55968
55969 Roo.grid.ColumnModel.defaultRenderer = function(value){
55970         if(typeof value == "string" && value.length < 1){
55971             return "&#160;";
55972         }
55973         return value;
55974 };
55975
55976 // Alias for backwards compatibility
55977 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55978 /*
55979  * Based on:
55980  * Ext JS Library 1.1.1
55981  * Copyright(c) 2006-2007, Ext JS, LLC.
55982  *
55983  * Originally Released Under LGPL - original licence link has changed is not relivant.
55984  *
55985  * Fork - LGPL
55986  * <script type="text/javascript">
55987  */
55988
55989 /**
55990  * @class Roo.grid.AbstractSelectionModel
55991  * @extends Roo.util.Observable
55992  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55993  * implemented by descendant classes.  This class should not be directly instantiated.
55994  * @constructor
55995  */
55996 Roo.grid.AbstractSelectionModel = function(){
55997     this.locked = false;
55998     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55999 };
56000
56001 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
56002     /** @ignore Called by the grid automatically. Do not call directly. */
56003     init : function(grid){
56004         this.grid = grid;
56005         this.initEvents();
56006     },
56007
56008     /**
56009      * Locks the selections.
56010      */
56011     lock : function(){
56012         this.locked = true;
56013     },
56014
56015     /**
56016      * Unlocks the selections.
56017      */
56018     unlock : function(){
56019         this.locked = false;
56020     },
56021
56022     /**
56023      * Returns true if the selections are locked.
56024      * @return {Boolean}
56025      */
56026     isLocked : function(){
56027         return this.locked;
56028     }
56029 });/*
56030  * Based on:
56031  * Ext JS Library 1.1.1
56032  * Copyright(c) 2006-2007, Ext JS, LLC.
56033  *
56034  * Originally Released Under LGPL - original licence link has changed is not relivant.
56035  *
56036  * Fork - LGPL
56037  * <script type="text/javascript">
56038  */
56039 /**
56040  * @extends Roo.grid.AbstractSelectionModel
56041  * @class Roo.grid.RowSelectionModel
56042  * The default SelectionModel used by {@link Roo.grid.Grid}.
56043  * It supports multiple selections and keyboard selection/navigation. 
56044  * @constructor
56045  * @param {Object} config
56046  */
56047 Roo.grid.RowSelectionModel = function(config){
56048     Roo.apply(this, config);
56049     this.selections = new Roo.util.MixedCollection(false, function(o){
56050         return o.id;
56051     });
56052
56053     this.last = false;
56054     this.lastActive = false;
56055
56056     this.addEvents({
56057         /**
56058              * @event selectionchange
56059              * Fires when the selection changes
56060              * @param {SelectionModel} this
56061              */
56062             "selectionchange" : true,
56063         /**
56064              * @event afterselectionchange
56065              * Fires after the selection changes (eg. by key press or clicking)
56066              * @param {SelectionModel} this
56067              */
56068             "afterselectionchange" : true,
56069         /**
56070              * @event beforerowselect
56071              * Fires when a row is selected being selected, return false to cancel.
56072              * @param {SelectionModel} this
56073              * @param {Number} rowIndex The selected index
56074              * @param {Boolean} keepExisting False if other selections will be cleared
56075              */
56076             "beforerowselect" : true,
56077         /**
56078              * @event rowselect
56079              * Fires when a row is selected.
56080              * @param {SelectionModel} this
56081              * @param {Number} rowIndex The selected index
56082              * @param {Roo.data.Record} r The record
56083              */
56084             "rowselect" : true,
56085         /**
56086              * @event rowdeselect
56087              * Fires when a row is deselected.
56088              * @param {SelectionModel} this
56089              * @param {Number} rowIndex The selected index
56090              */
56091         "rowdeselect" : true
56092     });
56093     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
56094     this.locked = false;
56095 };
56096
56097 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
56098     /**
56099      * @cfg {Boolean} singleSelect
56100      * True to allow selection of only one row at a time (defaults to false)
56101      */
56102     singleSelect : false,
56103
56104     // private
56105     initEvents : function(){
56106
56107         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
56108             this.grid.on("mousedown", this.handleMouseDown, this);
56109         }else{ // allow click to work like normal
56110             this.grid.on("rowclick", this.handleDragableRowClick, this);
56111         }
56112
56113         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
56114             "up" : function(e){
56115                 if(!e.shiftKey){
56116                     this.selectPrevious(e.shiftKey);
56117                 }else if(this.last !== false && this.lastActive !== false){
56118                     var last = this.last;
56119                     this.selectRange(this.last,  this.lastActive-1);
56120                     this.grid.getView().focusRow(this.lastActive);
56121                     if(last !== false){
56122                         this.last = last;
56123                     }
56124                 }else{
56125                     this.selectFirstRow();
56126                 }
56127                 this.fireEvent("afterselectionchange", this);
56128             },
56129             "down" : function(e){
56130                 if(!e.shiftKey){
56131                     this.selectNext(e.shiftKey);
56132                 }else if(this.last !== false && this.lastActive !== false){
56133                     var last = this.last;
56134                     this.selectRange(this.last,  this.lastActive+1);
56135                     this.grid.getView().focusRow(this.lastActive);
56136                     if(last !== false){
56137                         this.last = last;
56138                     }
56139                 }else{
56140                     this.selectFirstRow();
56141                 }
56142                 this.fireEvent("afterselectionchange", this);
56143             },
56144             scope: this
56145         });
56146
56147         var view = this.grid.view;
56148         view.on("refresh", this.onRefresh, this);
56149         view.on("rowupdated", this.onRowUpdated, this);
56150         view.on("rowremoved", this.onRemove, this);
56151     },
56152
56153     // private
56154     onRefresh : function(){
56155         var ds = this.grid.dataSource, i, v = this.grid.view;
56156         var s = this.selections;
56157         s.each(function(r){
56158             if((i = ds.indexOfId(r.id)) != -1){
56159                 v.onRowSelect(i);
56160                 s.add(ds.getAt(i)); // updating the selection relate data
56161             }else{
56162                 s.remove(r);
56163             }
56164         });
56165     },
56166
56167     // private
56168     onRemove : function(v, index, r){
56169         this.selections.remove(r);
56170     },
56171
56172     // private
56173     onRowUpdated : function(v, index, r){
56174         if(this.isSelected(r)){
56175             v.onRowSelect(index);
56176         }
56177     },
56178
56179     /**
56180      * Select records.
56181      * @param {Array} records The records to select
56182      * @param {Boolean} keepExisting (optional) True to keep existing selections
56183      */
56184     selectRecords : function(records, keepExisting){
56185         if(!keepExisting){
56186             this.clearSelections();
56187         }
56188         var ds = this.grid.dataSource;
56189         for(var i = 0, len = records.length; i < len; i++){
56190             this.selectRow(ds.indexOf(records[i]), true);
56191         }
56192     },
56193
56194     /**
56195      * Gets the number of selected rows.
56196      * @return {Number}
56197      */
56198     getCount : function(){
56199         return this.selections.length;
56200     },
56201
56202     /**
56203      * Selects the first row in the grid.
56204      */
56205     selectFirstRow : function(){
56206         this.selectRow(0);
56207     },
56208
56209     /**
56210      * Select the last row.
56211      * @param {Boolean} keepExisting (optional) True to keep existing selections
56212      */
56213     selectLastRow : function(keepExisting){
56214         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56215     },
56216
56217     /**
56218      * Selects the row immediately following the last selected row.
56219      * @param {Boolean} keepExisting (optional) True to keep existing selections
56220      */
56221     selectNext : function(keepExisting){
56222         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56223             this.selectRow(this.last+1, keepExisting);
56224             this.grid.getView().focusRow(this.last);
56225         }
56226     },
56227
56228     /**
56229      * Selects the row that precedes the last selected row.
56230      * @param {Boolean} keepExisting (optional) True to keep existing selections
56231      */
56232     selectPrevious : function(keepExisting){
56233         if(this.last){
56234             this.selectRow(this.last-1, keepExisting);
56235             this.grid.getView().focusRow(this.last);
56236         }
56237     },
56238
56239     /**
56240      * Returns the selected records
56241      * @return {Array} Array of selected records
56242      */
56243     getSelections : function(){
56244         return [].concat(this.selections.items);
56245     },
56246
56247     /**
56248      * Returns the first selected record.
56249      * @return {Record}
56250      */
56251     getSelected : function(){
56252         return this.selections.itemAt(0);
56253     },
56254
56255
56256     /**
56257      * Clears all selections.
56258      */
56259     clearSelections : function(fast){
56260         if(this.locked) {
56261             return;
56262         }
56263         if(fast !== true){
56264             var ds = this.grid.dataSource;
56265             var s = this.selections;
56266             s.each(function(r){
56267                 this.deselectRow(ds.indexOfId(r.id));
56268             }, this);
56269             s.clear();
56270         }else{
56271             this.selections.clear();
56272         }
56273         this.last = false;
56274     },
56275
56276
56277     /**
56278      * Selects all rows.
56279      */
56280     selectAll : function(){
56281         if(this.locked) {
56282             return;
56283         }
56284         this.selections.clear();
56285         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56286             this.selectRow(i, true);
56287         }
56288     },
56289
56290     /**
56291      * Returns True if there is a selection.
56292      * @return {Boolean}
56293      */
56294     hasSelection : function(){
56295         return this.selections.length > 0;
56296     },
56297
56298     /**
56299      * Returns True if the specified row is selected.
56300      * @param {Number/Record} record The record or index of the record to check
56301      * @return {Boolean}
56302      */
56303     isSelected : function(index){
56304         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56305         return (r && this.selections.key(r.id) ? true : false);
56306     },
56307
56308     /**
56309      * Returns True if the specified record id is selected.
56310      * @param {String} id The id of record to check
56311      * @return {Boolean}
56312      */
56313     isIdSelected : function(id){
56314         return (this.selections.key(id) ? true : false);
56315     },
56316
56317     // private
56318     handleMouseDown : function(e, t){
56319         var view = this.grid.getView(), rowIndex;
56320         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56321             return;
56322         };
56323         if(e.shiftKey && this.last !== false){
56324             var last = this.last;
56325             this.selectRange(last, rowIndex, e.ctrlKey);
56326             this.last = last; // reset the last
56327             view.focusRow(rowIndex);
56328         }else{
56329             var isSelected = this.isSelected(rowIndex);
56330             if(e.button !== 0 && isSelected){
56331                 view.focusRow(rowIndex);
56332             }else if(e.ctrlKey && isSelected){
56333                 this.deselectRow(rowIndex);
56334             }else if(!isSelected){
56335                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56336                 view.focusRow(rowIndex);
56337             }
56338         }
56339         this.fireEvent("afterselectionchange", this);
56340     },
56341     // private
56342     handleDragableRowClick :  function(grid, rowIndex, e) 
56343     {
56344         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56345             this.selectRow(rowIndex, false);
56346             grid.view.focusRow(rowIndex);
56347              this.fireEvent("afterselectionchange", this);
56348         }
56349     },
56350     
56351     /**
56352      * Selects multiple rows.
56353      * @param {Array} rows Array of the indexes of the row to select
56354      * @param {Boolean} keepExisting (optional) True to keep existing selections
56355      */
56356     selectRows : function(rows, keepExisting){
56357         if(!keepExisting){
56358             this.clearSelections();
56359         }
56360         for(var i = 0, len = rows.length; i < len; i++){
56361             this.selectRow(rows[i], true);
56362         }
56363     },
56364
56365     /**
56366      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56367      * @param {Number} startRow The index of the first row in the range
56368      * @param {Number} endRow The index of the last row in the range
56369      * @param {Boolean} keepExisting (optional) True to retain existing selections
56370      */
56371     selectRange : function(startRow, endRow, keepExisting){
56372         if(this.locked) {
56373             return;
56374         }
56375         if(!keepExisting){
56376             this.clearSelections();
56377         }
56378         if(startRow <= endRow){
56379             for(var i = startRow; i <= endRow; i++){
56380                 this.selectRow(i, true);
56381             }
56382         }else{
56383             for(var i = startRow; i >= endRow; i--){
56384                 this.selectRow(i, true);
56385             }
56386         }
56387     },
56388
56389     /**
56390      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56391      * @param {Number} startRow The index of the first row in the range
56392      * @param {Number} endRow The index of the last row in the range
56393      */
56394     deselectRange : function(startRow, endRow, preventViewNotify){
56395         if(this.locked) {
56396             return;
56397         }
56398         for(var i = startRow; i <= endRow; i++){
56399             this.deselectRow(i, preventViewNotify);
56400         }
56401     },
56402
56403     /**
56404      * Selects a row.
56405      * @param {Number} row The index of the row to select
56406      * @param {Boolean} keepExisting (optional) True to keep existing selections
56407      */
56408     selectRow : function(index, keepExisting, preventViewNotify){
56409         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
56410             return;
56411         }
56412         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56413             if(!keepExisting || this.singleSelect){
56414                 this.clearSelections();
56415             }
56416             var r = this.grid.dataSource.getAt(index);
56417             this.selections.add(r);
56418             this.last = this.lastActive = index;
56419             if(!preventViewNotify){
56420                 this.grid.getView().onRowSelect(index);
56421             }
56422             this.fireEvent("rowselect", this, index, r);
56423             this.fireEvent("selectionchange", this);
56424         }
56425     },
56426
56427     /**
56428      * Deselects a row.
56429      * @param {Number} row The index of the row to deselect
56430      */
56431     deselectRow : function(index, preventViewNotify){
56432         if(this.locked) {
56433             return;
56434         }
56435         if(this.last == index){
56436             this.last = false;
56437         }
56438         if(this.lastActive == index){
56439             this.lastActive = false;
56440         }
56441         var r = this.grid.dataSource.getAt(index);
56442         this.selections.remove(r);
56443         if(!preventViewNotify){
56444             this.grid.getView().onRowDeselect(index);
56445         }
56446         this.fireEvent("rowdeselect", this, index);
56447         this.fireEvent("selectionchange", this);
56448     },
56449
56450     // private
56451     restoreLast : function(){
56452         if(this._last){
56453             this.last = this._last;
56454         }
56455     },
56456
56457     // private
56458     acceptsNav : function(row, col, cm){
56459         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56460     },
56461
56462     // private
56463     onEditorKey : function(field, e){
56464         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56465         if(k == e.TAB){
56466             e.stopEvent();
56467             ed.completeEdit();
56468             if(e.shiftKey){
56469                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56470             }else{
56471                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56472             }
56473         }else if(k == e.ENTER && !e.ctrlKey){
56474             e.stopEvent();
56475             ed.completeEdit();
56476             if(e.shiftKey){
56477                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56478             }else{
56479                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56480             }
56481         }else if(k == e.ESC){
56482             ed.cancelEdit();
56483         }
56484         if(newCell){
56485             g.startEditing(newCell[0], newCell[1]);
56486         }
56487     }
56488 });/*
56489  * Based on:
56490  * Ext JS Library 1.1.1
56491  * Copyright(c) 2006-2007, Ext JS, LLC.
56492  *
56493  * Originally Released Under LGPL - original licence link has changed is not relivant.
56494  *
56495  * Fork - LGPL
56496  * <script type="text/javascript">
56497  */
56498 /**
56499  * @class Roo.grid.CellSelectionModel
56500  * @extends Roo.grid.AbstractSelectionModel
56501  * This class provides the basic implementation for cell selection in a grid.
56502  * @constructor
56503  * @param {Object} config The object containing the configuration of this model.
56504  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56505  */
56506 Roo.grid.CellSelectionModel = function(config){
56507     Roo.apply(this, config);
56508
56509     this.selection = null;
56510
56511     this.addEvents({
56512         /**
56513              * @event beforerowselect
56514              * Fires before a cell is selected.
56515              * @param {SelectionModel} this
56516              * @param {Number} rowIndex The selected row index
56517              * @param {Number} colIndex The selected cell index
56518              */
56519             "beforecellselect" : true,
56520         /**
56521              * @event cellselect
56522              * Fires when a cell is selected.
56523              * @param {SelectionModel} this
56524              * @param {Number} rowIndex The selected row index
56525              * @param {Number} colIndex The selected cell index
56526              */
56527             "cellselect" : true,
56528         /**
56529              * @event selectionchange
56530              * Fires when the active selection changes.
56531              * @param {SelectionModel} this
56532              * @param {Object} selection null for no selection or an object (o) with two properties
56533                 <ul>
56534                 <li>o.record: the record object for the row the selection is in</li>
56535                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56536                 </ul>
56537              */
56538             "selectionchange" : true,
56539         /**
56540              * @event tabend
56541              * Fires when the tab (or enter) was pressed on the last editable cell
56542              * You can use this to trigger add new row.
56543              * @param {SelectionModel} this
56544              */
56545             "tabend" : true,
56546          /**
56547              * @event beforeeditnext
56548              * Fires before the next editable sell is made active
56549              * You can use this to skip to another cell or fire the tabend
56550              *    if you set cell to false
56551              * @param {Object} eventdata object : { cell : [ row, col ] } 
56552              */
56553             "beforeeditnext" : true
56554     });
56555     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56556 };
56557
56558 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56559     
56560     enter_is_tab: false,
56561
56562     /** @ignore */
56563     initEvents : function(){
56564         this.grid.on("mousedown", this.handleMouseDown, this);
56565         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56566         var view = this.grid.view;
56567         view.on("refresh", this.onViewChange, this);
56568         view.on("rowupdated", this.onRowUpdated, this);
56569         view.on("beforerowremoved", this.clearSelections, this);
56570         view.on("beforerowsinserted", this.clearSelections, this);
56571         if(this.grid.isEditor){
56572             this.grid.on("beforeedit", this.beforeEdit,  this);
56573         }
56574     },
56575
56576         //private
56577     beforeEdit : function(e){
56578         this.select(e.row, e.column, false, true, e.record);
56579     },
56580
56581         //private
56582     onRowUpdated : function(v, index, r){
56583         if(this.selection && this.selection.record == r){
56584             v.onCellSelect(index, this.selection.cell[1]);
56585         }
56586     },
56587
56588         //private
56589     onViewChange : function(){
56590         this.clearSelections(true);
56591     },
56592
56593         /**
56594          * Returns the currently selected cell,.
56595          * @return {Array} The selected cell (row, column) or null if none selected.
56596          */
56597     getSelectedCell : function(){
56598         return this.selection ? this.selection.cell : null;
56599     },
56600
56601     /**
56602      * Clears all selections.
56603      * @param {Boolean} true to prevent the gridview from being notified about the change.
56604      */
56605     clearSelections : function(preventNotify){
56606         var s = this.selection;
56607         if(s){
56608             if(preventNotify !== true){
56609                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56610             }
56611             this.selection = null;
56612             this.fireEvent("selectionchange", this, null);
56613         }
56614     },
56615
56616     /**
56617      * Returns true if there is a selection.
56618      * @return {Boolean}
56619      */
56620     hasSelection : function(){
56621         return this.selection ? true : false;
56622     },
56623
56624     /** @ignore */
56625     handleMouseDown : function(e, t){
56626         var v = this.grid.getView();
56627         if(this.isLocked()){
56628             return;
56629         };
56630         var row = v.findRowIndex(t);
56631         var cell = v.findCellIndex(t);
56632         if(row !== false && cell !== false){
56633             this.select(row, cell);
56634         }
56635     },
56636
56637     /**
56638      * Selects a cell.
56639      * @param {Number} rowIndex
56640      * @param {Number} collIndex
56641      */
56642     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56643         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56644             this.clearSelections();
56645             r = r || this.grid.dataSource.getAt(rowIndex);
56646             this.selection = {
56647                 record : r,
56648                 cell : [rowIndex, colIndex]
56649             };
56650             if(!preventViewNotify){
56651                 var v = this.grid.getView();
56652                 v.onCellSelect(rowIndex, colIndex);
56653                 if(preventFocus !== true){
56654                     v.focusCell(rowIndex, colIndex);
56655                 }
56656             }
56657             this.fireEvent("cellselect", this, rowIndex, colIndex);
56658             this.fireEvent("selectionchange", this, this.selection);
56659         }
56660     },
56661
56662         //private
56663     isSelectable : function(rowIndex, colIndex, cm){
56664         return !cm.isHidden(colIndex);
56665     },
56666
56667     /** @ignore */
56668     handleKeyDown : function(e){
56669         //Roo.log('Cell Sel Model handleKeyDown');
56670         if(!e.isNavKeyPress()){
56671             return;
56672         }
56673         var g = this.grid, s = this.selection;
56674         if(!s){
56675             e.stopEvent();
56676             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56677             if(cell){
56678                 this.select(cell[0], cell[1]);
56679             }
56680             return;
56681         }
56682         var sm = this;
56683         var walk = function(row, col, step){
56684             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56685         };
56686         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56687         var newCell;
56688
56689       
56690
56691         switch(k){
56692             case e.TAB:
56693                 // handled by onEditorKey
56694                 if (g.isEditor && g.editing) {
56695                     return;
56696                 }
56697                 if(e.shiftKey) {
56698                     newCell = walk(r, c-1, -1);
56699                 } else {
56700                     newCell = walk(r, c+1, 1);
56701                 }
56702                 break;
56703             
56704             case e.DOWN:
56705                newCell = walk(r+1, c, 1);
56706                 break;
56707             
56708             case e.UP:
56709                 newCell = walk(r-1, c, -1);
56710                 break;
56711             
56712             case e.RIGHT:
56713                 newCell = walk(r, c+1, 1);
56714                 break;
56715             
56716             case e.LEFT:
56717                 newCell = walk(r, c-1, -1);
56718                 break;
56719             
56720             case e.ENTER:
56721                 
56722                 if(g.isEditor && !g.editing){
56723                    g.startEditing(r, c);
56724                    e.stopEvent();
56725                    return;
56726                 }
56727                 
56728                 
56729              break;
56730         };
56731         if(newCell){
56732             this.select(newCell[0], newCell[1]);
56733             e.stopEvent();
56734             
56735         }
56736     },
56737
56738     acceptsNav : function(row, col, cm){
56739         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56740     },
56741     /**
56742      * Selects a cell.
56743      * @param {Number} field (not used) - as it's normally used as a listener
56744      * @param {Number} e - event - fake it by using
56745      *
56746      * var e = Roo.EventObjectImpl.prototype;
56747      * e.keyCode = e.TAB
56748      *
56749      * 
56750      */
56751     onEditorKey : function(field, e){
56752         
56753         var k = e.getKey(),
56754             newCell,
56755             g = this.grid,
56756             ed = g.activeEditor,
56757             forward = false;
56758         ///Roo.log('onEditorKey' + k);
56759         
56760         
56761         if (this.enter_is_tab && k == e.ENTER) {
56762             k = e.TAB;
56763         }
56764         
56765         if(k == e.TAB){
56766             if(e.shiftKey){
56767                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56768             }else{
56769                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56770                 forward = true;
56771             }
56772             
56773             e.stopEvent();
56774             
56775         } else if(k == e.ENTER &&  !e.ctrlKey){
56776             ed.completeEdit();
56777             e.stopEvent();
56778             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56779         
56780                 } else if(k == e.ESC){
56781             ed.cancelEdit();
56782         }
56783                 
56784         if (newCell) {
56785             var ecall = { cell : newCell, forward : forward };
56786             this.fireEvent('beforeeditnext', ecall );
56787             newCell = ecall.cell;
56788                         forward = ecall.forward;
56789         }
56790                 
56791         if(newCell){
56792             //Roo.log('next cell after edit');
56793             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56794         } else if (forward) {
56795             // tabbed past last
56796             this.fireEvent.defer(100, this, ['tabend',this]);
56797         }
56798     }
56799 });/*
56800  * Based on:
56801  * Ext JS Library 1.1.1
56802  * Copyright(c) 2006-2007, Ext JS, LLC.
56803  *
56804  * Originally Released Under LGPL - original licence link has changed is not relivant.
56805  *
56806  * Fork - LGPL
56807  * <script type="text/javascript">
56808  */
56809  
56810 /**
56811  * @class Roo.grid.EditorGrid
56812  * @extends Roo.grid.Grid
56813  * Class for creating and editable grid.
56814  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56815  * The container MUST have some type of size defined for the grid to fill. The container will be 
56816  * automatically set to position relative if it isn't already.
56817  * @param {Object} dataSource The data model to bind to
56818  * @param {Object} colModel The column model with info about this grid's columns
56819  */
56820 Roo.grid.EditorGrid = function(container, config){
56821     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56822     this.getGridEl().addClass("xedit-grid");
56823
56824     if(!this.selModel){
56825         this.selModel = new Roo.grid.CellSelectionModel();
56826     }
56827
56828     this.activeEditor = null;
56829
56830         this.addEvents({
56831             /**
56832              * @event beforeedit
56833              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56834              * <ul style="padding:5px;padding-left:16px;">
56835              * <li>grid - This grid</li>
56836              * <li>record - The record being edited</li>
56837              * <li>field - The field name being edited</li>
56838              * <li>value - The value for the field being edited.</li>
56839              * <li>row - The grid row index</li>
56840              * <li>column - The grid column index</li>
56841              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56842              * </ul>
56843              * @param {Object} e An edit event (see above for description)
56844              */
56845             "beforeedit" : true,
56846             /**
56847              * @event afteredit
56848              * Fires after a cell is edited. <br />
56849              * <ul style="padding:5px;padding-left:16px;">
56850              * <li>grid - This grid</li>
56851              * <li>record - The record being edited</li>
56852              * <li>field - The field name being edited</li>
56853              * <li>value - The value being set</li>
56854              * <li>originalValue - The original value for the field, before the edit.</li>
56855              * <li>row - The grid row index</li>
56856              * <li>column - The grid column index</li>
56857              * </ul>
56858              * @param {Object} e An edit event (see above for description)
56859              */
56860             "afteredit" : true,
56861             /**
56862              * @event validateedit
56863              * Fires after a cell is edited, but before the value is set in the record. 
56864          * You can use this to modify the value being set in the field, Return false
56865              * to cancel the change. The edit event object has the following properties <br />
56866              * <ul style="padding:5px;padding-left:16px;">
56867          * <li>editor - This editor</li>
56868              * <li>grid - This grid</li>
56869              * <li>record - The record being edited</li>
56870              * <li>field - The field name being edited</li>
56871              * <li>value - The value being set</li>
56872              * <li>originalValue - The original value for the field, before the edit.</li>
56873              * <li>row - The grid row index</li>
56874              * <li>column - The grid column index</li>
56875              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56876              * </ul>
56877              * @param {Object} e An edit event (see above for description)
56878              */
56879             "validateedit" : true
56880         });
56881     this.on("bodyscroll", this.stopEditing,  this);
56882     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56883 };
56884
56885 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56886     /**
56887      * @cfg {Number} clicksToEdit
56888      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56889      */
56890     clicksToEdit: 2,
56891
56892     // private
56893     isEditor : true,
56894     // private
56895     trackMouseOver: false, // causes very odd FF errors
56896
56897     onCellDblClick : function(g, row, col){
56898         this.startEditing(row, col);
56899     },
56900
56901     onEditComplete : function(ed, value, startValue){
56902         this.editing = false;
56903         this.activeEditor = null;
56904         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56905         var r = ed.record;
56906         var field = this.colModel.getDataIndex(ed.col);
56907         var e = {
56908             grid: this,
56909             record: r,
56910             field: field,
56911             originalValue: startValue,
56912             value: value,
56913             row: ed.row,
56914             column: ed.col,
56915             cancel:false,
56916             editor: ed
56917         };
56918         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
56919         cell.show();
56920           
56921         if(String(value) !== String(startValue)){
56922             
56923             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56924                 r.set(field, e.value);
56925                 // if we are dealing with a combo box..
56926                 // then we also set the 'name' colum to be the displayField
56927                 if (ed.field.displayField && ed.field.name) {
56928                     r.set(ed.field.name, ed.field.el.dom.value);
56929                 }
56930                 
56931                 delete e.cancel; //?? why!!!
56932                 this.fireEvent("afteredit", e);
56933             }
56934         } else {
56935             this.fireEvent("afteredit", e); // always fire it!
56936         }
56937         this.view.focusCell(ed.row, ed.col);
56938     },
56939
56940     /**
56941      * Starts editing the specified for the specified row/column
56942      * @param {Number} rowIndex
56943      * @param {Number} colIndex
56944      */
56945     startEditing : function(row, col){
56946         this.stopEditing();
56947         if(this.colModel.isCellEditable(col, row)){
56948             this.view.ensureVisible(row, col, true);
56949           
56950             var r = this.dataSource.getAt(row);
56951             var field = this.colModel.getDataIndex(col);
56952             var cell = Roo.get(this.view.getCell(row,col));
56953             var e = {
56954                 grid: this,
56955                 record: r,
56956                 field: field,
56957                 value: r.data[field],
56958                 row: row,
56959                 column: col,
56960                 cancel:false 
56961             };
56962             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56963                 this.editing = true;
56964                 var ed = this.colModel.getCellEditor(col, row);
56965                 
56966                 if (!ed) {
56967                     return;
56968                 }
56969                 if(!ed.rendered){
56970                     ed.render(ed.parentEl || document.body);
56971                 }
56972                 ed.field.reset();
56973                
56974                 cell.hide();
56975                 
56976                 (function(){ // complex but required for focus issues in safari, ie and opera
56977                     ed.row = row;
56978                     ed.col = col;
56979                     ed.record = r;
56980                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56981                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56982                     this.activeEditor = ed;
56983                     var v = r.data[field];
56984                     ed.startEdit(this.view.getCell(row, col), v);
56985                     // combo's with 'displayField and name set
56986                     if (ed.field.displayField && ed.field.name) {
56987                         ed.field.el.dom.value = r.data[ed.field.name];
56988                     }
56989                     
56990                     
56991                 }).defer(50, this);
56992             }
56993         }
56994     },
56995         
56996     /**
56997      * Stops any active editing
56998      */
56999     stopEditing : function(){
57000         if(this.activeEditor){
57001             this.activeEditor.completeEdit();
57002         }
57003         this.activeEditor = null;
57004     },
57005         
57006          /**
57007      * Called to get grid's drag proxy text, by default returns this.ddText.
57008      * @return {String}
57009      */
57010     getDragDropText : function(){
57011         var count = this.selModel.getSelectedCell() ? 1 : 0;
57012         return String.format(this.ddText, count, count == 1 ? '' : 's');
57013     }
57014         
57015 });/*
57016  * Based on:
57017  * Ext JS Library 1.1.1
57018  * Copyright(c) 2006-2007, Ext JS, LLC.
57019  *
57020  * Originally Released Under LGPL - original licence link has changed is not relivant.
57021  *
57022  * Fork - LGPL
57023  * <script type="text/javascript">
57024  */
57025
57026 // private - not really -- you end up using it !
57027 // This is a support class used internally by the Grid components
57028
57029 /**
57030  * @class Roo.grid.GridEditor
57031  * @extends Roo.Editor
57032  * Class for creating and editable grid elements.
57033  * @param {Object} config any settings (must include field)
57034  */
57035 Roo.grid.GridEditor = function(field, config){
57036     if (!config && field.field) {
57037         config = field;
57038         field = Roo.factory(config.field, Roo.form);
57039     }
57040     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
57041     field.monitorTab = false;
57042 };
57043
57044 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
57045     
57046     /**
57047      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
57048      */
57049     
57050     alignment: "tl-tl",
57051     autoSize: "width",
57052     hideEl : false,
57053     cls: "x-small-editor x-grid-editor",
57054     shim:false,
57055     shadow:"frame"
57056 });/*
57057  * Based on:
57058  * Ext JS Library 1.1.1
57059  * Copyright(c) 2006-2007, Ext JS, LLC.
57060  *
57061  * Originally Released Under LGPL - original licence link has changed is not relivant.
57062  *
57063  * Fork - LGPL
57064  * <script type="text/javascript">
57065  */
57066   
57067
57068   
57069 Roo.grid.PropertyRecord = Roo.data.Record.create([
57070     {name:'name',type:'string'},  'value'
57071 ]);
57072
57073
57074 Roo.grid.PropertyStore = function(grid, source){
57075     this.grid = grid;
57076     this.store = new Roo.data.Store({
57077         recordType : Roo.grid.PropertyRecord
57078     });
57079     this.store.on('update', this.onUpdate,  this);
57080     if(source){
57081         this.setSource(source);
57082     }
57083     Roo.grid.PropertyStore.superclass.constructor.call(this);
57084 };
57085
57086
57087
57088 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
57089     setSource : function(o){
57090         this.source = o;
57091         this.store.removeAll();
57092         var data = [];
57093         for(var k in o){
57094             if(this.isEditableValue(o[k])){
57095                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
57096             }
57097         }
57098         this.store.loadRecords({records: data}, {}, true);
57099     },
57100
57101     onUpdate : function(ds, record, type){
57102         if(type == Roo.data.Record.EDIT){
57103             var v = record.data['value'];
57104             var oldValue = record.modified['value'];
57105             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
57106                 this.source[record.id] = v;
57107                 record.commit();
57108                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
57109             }else{
57110                 record.reject();
57111             }
57112         }
57113     },
57114
57115     getProperty : function(row){
57116        return this.store.getAt(row);
57117     },
57118
57119     isEditableValue: function(val){
57120         if(val && val instanceof Date){
57121             return true;
57122         }else if(typeof val == 'object' || typeof val == 'function'){
57123             return false;
57124         }
57125         return true;
57126     },
57127
57128     setValue : function(prop, value){
57129         this.source[prop] = value;
57130         this.store.getById(prop).set('value', value);
57131     },
57132
57133     getSource : function(){
57134         return this.source;
57135     }
57136 });
57137
57138 Roo.grid.PropertyColumnModel = function(grid, store){
57139     this.grid = grid;
57140     var g = Roo.grid;
57141     g.PropertyColumnModel.superclass.constructor.call(this, [
57142         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
57143         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
57144     ]);
57145     this.store = store;
57146     this.bselect = Roo.DomHelper.append(document.body, {
57147         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
57148             {tag: 'option', value: 'true', html: 'true'},
57149             {tag: 'option', value: 'false', html: 'false'}
57150         ]
57151     });
57152     Roo.id(this.bselect);
57153     var f = Roo.form;
57154     this.editors = {
57155         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
57156         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
57157         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
57158         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
57159         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
57160     };
57161     this.renderCellDelegate = this.renderCell.createDelegate(this);
57162     this.renderPropDelegate = this.renderProp.createDelegate(this);
57163 };
57164
57165 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
57166     
57167     
57168     nameText : 'Name',
57169     valueText : 'Value',
57170     
57171     dateFormat : 'm/j/Y',
57172     
57173     
57174     renderDate : function(dateVal){
57175         return dateVal.dateFormat(this.dateFormat);
57176     },
57177
57178     renderBool : function(bVal){
57179         return bVal ? 'true' : 'false';
57180     },
57181
57182     isCellEditable : function(colIndex, rowIndex){
57183         return colIndex == 1;
57184     },
57185
57186     getRenderer : function(col){
57187         return col == 1 ?
57188             this.renderCellDelegate : this.renderPropDelegate;
57189     },
57190
57191     renderProp : function(v){
57192         return this.getPropertyName(v);
57193     },
57194
57195     renderCell : function(val){
57196         var rv = val;
57197         if(val instanceof Date){
57198             rv = this.renderDate(val);
57199         }else if(typeof val == 'boolean'){
57200             rv = this.renderBool(val);
57201         }
57202         return Roo.util.Format.htmlEncode(rv);
57203     },
57204
57205     getPropertyName : function(name){
57206         var pn = this.grid.propertyNames;
57207         return pn && pn[name] ? pn[name] : name;
57208     },
57209
57210     getCellEditor : function(colIndex, rowIndex){
57211         var p = this.store.getProperty(rowIndex);
57212         var n = p.data['name'], val = p.data['value'];
57213         
57214         if(typeof(this.grid.customEditors[n]) == 'string'){
57215             return this.editors[this.grid.customEditors[n]];
57216         }
57217         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57218             return this.grid.customEditors[n];
57219         }
57220         if(val instanceof Date){
57221             return this.editors['date'];
57222         }else if(typeof val == 'number'){
57223             return this.editors['number'];
57224         }else if(typeof val == 'boolean'){
57225             return this.editors['boolean'];
57226         }else{
57227             return this.editors['string'];
57228         }
57229     }
57230 });
57231
57232 /**
57233  * @class Roo.grid.PropertyGrid
57234  * @extends Roo.grid.EditorGrid
57235  * This class represents the  interface of a component based property grid control.
57236  * <br><br>Usage:<pre><code>
57237  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57238       
57239  });
57240  // set any options
57241  grid.render();
57242  * </code></pre>
57243   
57244  * @constructor
57245  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57246  * The container MUST have some type of size defined for the grid to fill. The container will be
57247  * automatically set to position relative if it isn't already.
57248  * @param {Object} config A config object that sets properties on this grid.
57249  */
57250 Roo.grid.PropertyGrid = function(container, config){
57251     config = config || {};
57252     var store = new Roo.grid.PropertyStore(this);
57253     this.store = store;
57254     var cm = new Roo.grid.PropertyColumnModel(this, store);
57255     store.store.sort('name', 'ASC');
57256     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57257         ds: store.store,
57258         cm: cm,
57259         enableColLock:false,
57260         enableColumnMove:false,
57261         stripeRows:false,
57262         trackMouseOver: false,
57263         clicksToEdit:1
57264     }, config));
57265     this.getGridEl().addClass('x-props-grid');
57266     this.lastEditRow = null;
57267     this.on('columnresize', this.onColumnResize, this);
57268     this.addEvents({
57269          /**
57270              * @event beforepropertychange
57271              * Fires before a property changes (return false to stop?)
57272              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57273              * @param {String} id Record Id
57274              * @param {String} newval New Value
57275          * @param {String} oldval Old Value
57276              */
57277         "beforepropertychange": true,
57278         /**
57279              * @event propertychange
57280              * Fires after a property changes
57281              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57282              * @param {String} id Record Id
57283              * @param {String} newval New Value
57284          * @param {String} oldval Old Value
57285              */
57286         "propertychange": true
57287     });
57288     this.customEditors = this.customEditors || {};
57289 };
57290 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57291     
57292      /**
57293      * @cfg {Object} customEditors map of colnames=> custom editors.
57294      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57295      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57296      * false disables editing of the field.
57297          */
57298     
57299       /**
57300      * @cfg {Object} propertyNames map of property Names to their displayed value
57301          */
57302     
57303     render : function(){
57304         Roo.grid.PropertyGrid.superclass.render.call(this);
57305         this.autoSize.defer(100, this);
57306     },
57307
57308     autoSize : function(){
57309         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57310         if(this.view){
57311             this.view.fitColumns();
57312         }
57313     },
57314
57315     onColumnResize : function(){
57316         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57317         this.autoSize();
57318     },
57319     /**
57320      * Sets the data for the Grid
57321      * accepts a Key => Value object of all the elements avaiable.
57322      * @param {Object} data  to appear in grid.
57323      */
57324     setSource : function(source){
57325         this.store.setSource(source);
57326         //this.autoSize();
57327     },
57328     /**
57329      * Gets all the data from the grid.
57330      * @return {Object} data  data stored in grid
57331      */
57332     getSource : function(){
57333         return this.store.getSource();
57334     }
57335 });/*
57336   
57337  * Licence LGPL
57338  
57339  */
57340  
57341 /**
57342  * @class Roo.grid.Calendar
57343  * @extends Roo.util.Grid
57344  * This class extends the Grid to provide a calendar widget
57345  * <br><br>Usage:<pre><code>
57346  var grid = new Roo.grid.Calendar("my-container-id", {
57347      ds: myDataStore,
57348      cm: myColModel,
57349      selModel: mySelectionModel,
57350      autoSizeColumns: true,
57351      monitorWindowResize: false,
57352      trackMouseOver: true
57353      eventstore : real data store..
57354  });
57355  // set any options
57356  grid.render();
57357   
57358   * @constructor
57359  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57360  * The container MUST have some type of size defined for the grid to fill. The container will be
57361  * automatically set to position relative if it isn't already.
57362  * @param {Object} config A config object that sets properties on this grid.
57363  */
57364 Roo.grid.Calendar = function(container, config){
57365         // initialize the container
57366         this.container = Roo.get(container);
57367         this.container.update("");
57368         this.container.setStyle("overflow", "hidden");
57369     this.container.addClass('x-grid-container');
57370
57371     this.id = this.container.id;
57372
57373     Roo.apply(this, config);
57374     // check and correct shorthanded configs
57375     
57376     var rows = [];
57377     var d =1;
57378     for (var r = 0;r < 6;r++) {
57379         
57380         rows[r]=[];
57381         for (var c =0;c < 7;c++) {
57382             rows[r][c]= '';
57383         }
57384     }
57385     if (this.eventStore) {
57386         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57387         this.eventStore.on('load',this.onLoad, this);
57388         this.eventStore.on('beforeload',this.clearEvents, this);
57389          
57390     }
57391     
57392     this.dataSource = new Roo.data.Store({
57393             proxy: new Roo.data.MemoryProxy(rows),
57394             reader: new Roo.data.ArrayReader({}, [
57395                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57396     });
57397
57398     this.dataSource.load();
57399     this.ds = this.dataSource;
57400     this.ds.xmodule = this.xmodule || false;
57401     
57402     
57403     var cellRender = function(v,x,r)
57404     {
57405         return String.format(
57406             '<div class="fc-day  fc-widget-content"><div>' +
57407                 '<div class="fc-event-container"></div>' +
57408                 '<div class="fc-day-number">{0}</div>'+
57409                 
57410                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57411             '</div></div>', v);
57412     
57413     }
57414     
57415     
57416     this.colModel = new Roo.grid.ColumnModel( [
57417         {
57418             xtype: 'ColumnModel',
57419             xns: Roo.grid,
57420             dataIndex : 'weekday0',
57421             header : 'Sunday',
57422             renderer : cellRender
57423         },
57424         {
57425             xtype: 'ColumnModel',
57426             xns: Roo.grid,
57427             dataIndex : 'weekday1',
57428             header : 'Monday',
57429             renderer : cellRender
57430         },
57431         {
57432             xtype: 'ColumnModel',
57433             xns: Roo.grid,
57434             dataIndex : 'weekday2',
57435             header : 'Tuesday',
57436             renderer : cellRender
57437         },
57438         {
57439             xtype: 'ColumnModel',
57440             xns: Roo.grid,
57441             dataIndex : 'weekday3',
57442             header : 'Wednesday',
57443             renderer : cellRender
57444         },
57445         {
57446             xtype: 'ColumnModel',
57447             xns: Roo.grid,
57448             dataIndex : 'weekday4',
57449             header : 'Thursday',
57450             renderer : cellRender
57451         },
57452         {
57453             xtype: 'ColumnModel',
57454             xns: Roo.grid,
57455             dataIndex : 'weekday5',
57456             header : 'Friday',
57457             renderer : cellRender
57458         },
57459         {
57460             xtype: 'ColumnModel',
57461             xns: Roo.grid,
57462             dataIndex : 'weekday6',
57463             header : 'Saturday',
57464             renderer : cellRender
57465         }
57466     ]);
57467     this.cm = this.colModel;
57468     this.cm.xmodule = this.xmodule || false;
57469  
57470         
57471           
57472     //this.selModel = new Roo.grid.CellSelectionModel();
57473     //this.sm = this.selModel;
57474     //this.selModel.init(this);
57475     
57476     
57477     if(this.width){
57478         this.container.setWidth(this.width);
57479     }
57480
57481     if(this.height){
57482         this.container.setHeight(this.height);
57483     }
57484     /** @private */
57485         this.addEvents({
57486         // raw events
57487         /**
57488          * @event click
57489          * The raw click event for the entire grid.
57490          * @param {Roo.EventObject} e
57491          */
57492         "click" : true,
57493         /**
57494          * @event dblclick
57495          * The raw dblclick event for the entire grid.
57496          * @param {Roo.EventObject} e
57497          */
57498         "dblclick" : true,
57499         /**
57500          * @event contextmenu
57501          * The raw contextmenu event for the entire grid.
57502          * @param {Roo.EventObject} e
57503          */
57504         "contextmenu" : true,
57505         /**
57506          * @event mousedown
57507          * The raw mousedown event for the entire grid.
57508          * @param {Roo.EventObject} e
57509          */
57510         "mousedown" : true,
57511         /**
57512          * @event mouseup
57513          * The raw mouseup event for the entire grid.
57514          * @param {Roo.EventObject} e
57515          */
57516         "mouseup" : true,
57517         /**
57518          * @event mouseover
57519          * The raw mouseover event for the entire grid.
57520          * @param {Roo.EventObject} e
57521          */
57522         "mouseover" : true,
57523         /**
57524          * @event mouseout
57525          * The raw mouseout event for the entire grid.
57526          * @param {Roo.EventObject} e
57527          */
57528         "mouseout" : true,
57529         /**
57530          * @event keypress
57531          * The raw keypress event for the entire grid.
57532          * @param {Roo.EventObject} e
57533          */
57534         "keypress" : true,
57535         /**
57536          * @event keydown
57537          * The raw keydown event for the entire grid.
57538          * @param {Roo.EventObject} e
57539          */
57540         "keydown" : true,
57541
57542         // custom events
57543
57544         /**
57545          * @event cellclick
57546          * Fires when a cell is clicked
57547          * @param {Grid} this
57548          * @param {Number} rowIndex
57549          * @param {Number} columnIndex
57550          * @param {Roo.EventObject} e
57551          */
57552         "cellclick" : true,
57553         /**
57554          * @event celldblclick
57555          * Fires when a cell is double clicked
57556          * @param {Grid} this
57557          * @param {Number} rowIndex
57558          * @param {Number} columnIndex
57559          * @param {Roo.EventObject} e
57560          */
57561         "celldblclick" : true,
57562         /**
57563          * @event rowclick
57564          * Fires when a row is clicked
57565          * @param {Grid} this
57566          * @param {Number} rowIndex
57567          * @param {Roo.EventObject} e
57568          */
57569         "rowclick" : true,
57570         /**
57571          * @event rowdblclick
57572          * Fires when a row is double clicked
57573          * @param {Grid} this
57574          * @param {Number} rowIndex
57575          * @param {Roo.EventObject} e
57576          */
57577         "rowdblclick" : true,
57578         /**
57579          * @event headerclick
57580          * Fires when a header is clicked
57581          * @param {Grid} this
57582          * @param {Number} columnIndex
57583          * @param {Roo.EventObject} e
57584          */
57585         "headerclick" : true,
57586         /**
57587          * @event headerdblclick
57588          * Fires when a header cell is double clicked
57589          * @param {Grid} this
57590          * @param {Number} columnIndex
57591          * @param {Roo.EventObject} e
57592          */
57593         "headerdblclick" : true,
57594         /**
57595          * @event rowcontextmenu
57596          * Fires when a row is right clicked
57597          * @param {Grid} this
57598          * @param {Number} rowIndex
57599          * @param {Roo.EventObject} e
57600          */
57601         "rowcontextmenu" : true,
57602         /**
57603          * @event cellcontextmenu
57604          * Fires when a cell is right clicked
57605          * @param {Grid} this
57606          * @param {Number} rowIndex
57607          * @param {Number} cellIndex
57608          * @param {Roo.EventObject} e
57609          */
57610          "cellcontextmenu" : true,
57611         /**
57612          * @event headercontextmenu
57613          * Fires when a header is right clicked
57614          * @param {Grid} this
57615          * @param {Number} columnIndex
57616          * @param {Roo.EventObject} e
57617          */
57618         "headercontextmenu" : true,
57619         /**
57620          * @event bodyscroll
57621          * Fires when the body element is scrolled
57622          * @param {Number} scrollLeft
57623          * @param {Number} scrollTop
57624          */
57625         "bodyscroll" : true,
57626         /**
57627          * @event columnresize
57628          * Fires when the user resizes a column
57629          * @param {Number} columnIndex
57630          * @param {Number} newSize
57631          */
57632         "columnresize" : true,
57633         /**
57634          * @event columnmove
57635          * Fires when the user moves a column
57636          * @param {Number} oldIndex
57637          * @param {Number} newIndex
57638          */
57639         "columnmove" : true,
57640         /**
57641          * @event startdrag
57642          * Fires when row(s) start being dragged
57643          * @param {Grid} this
57644          * @param {Roo.GridDD} dd The drag drop object
57645          * @param {event} e The raw browser event
57646          */
57647         "startdrag" : true,
57648         /**
57649          * @event enddrag
57650          * Fires when a drag operation is complete
57651          * @param {Grid} this
57652          * @param {Roo.GridDD} dd The drag drop object
57653          * @param {event} e The raw browser event
57654          */
57655         "enddrag" : true,
57656         /**
57657          * @event dragdrop
57658          * Fires when dragged row(s) are dropped on a valid DD target
57659          * @param {Grid} this
57660          * @param {Roo.GridDD} dd The drag drop object
57661          * @param {String} targetId The target drag drop object
57662          * @param {event} e The raw browser event
57663          */
57664         "dragdrop" : true,
57665         /**
57666          * @event dragover
57667          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57668          * @param {Grid} this
57669          * @param {Roo.GridDD} dd The drag drop object
57670          * @param {String} targetId The target drag drop object
57671          * @param {event} e The raw browser event
57672          */
57673         "dragover" : true,
57674         /**
57675          * @event dragenter
57676          *  Fires when the dragged row(s) first cross another DD target while being dragged
57677          * @param {Grid} this
57678          * @param {Roo.GridDD} dd The drag drop object
57679          * @param {String} targetId The target drag drop object
57680          * @param {event} e The raw browser event
57681          */
57682         "dragenter" : true,
57683         /**
57684          * @event dragout
57685          * Fires when the dragged row(s) leave another DD target while being dragged
57686          * @param {Grid} this
57687          * @param {Roo.GridDD} dd The drag drop object
57688          * @param {String} targetId The target drag drop object
57689          * @param {event} e The raw browser event
57690          */
57691         "dragout" : true,
57692         /**
57693          * @event rowclass
57694          * Fires when a row is rendered, so you can change add a style to it.
57695          * @param {GridView} gridview   The grid view
57696          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57697          */
57698         'rowclass' : true,
57699
57700         /**
57701          * @event render
57702          * Fires when the grid is rendered
57703          * @param {Grid} grid
57704          */
57705         'render' : true,
57706             /**
57707              * @event select
57708              * Fires when a date is selected
57709              * @param {DatePicker} this
57710              * @param {Date} date The selected date
57711              */
57712         'select': true,
57713         /**
57714              * @event monthchange
57715              * Fires when the displayed month changes 
57716              * @param {DatePicker} this
57717              * @param {Date} date The selected month
57718              */
57719         'monthchange': true,
57720         /**
57721              * @event evententer
57722              * Fires when mouse over an event
57723              * @param {Calendar} this
57724              * @param {event} Event
57725              */
57726         'evententer': true,
57727         /**
57728              * @event eventleave
57729              * Fires when the mouse leaves an
57730              * @param {Calendar} this
57731              * @param {event}
57732              */
57733         'eventleave': true,
57734         /**
57735              * @event eventclick
57736              * Fires when the mouse click an
57737              * @param {Calendar} this
57738              * @param {event}
57739              */
57740         'eventclick': true,
57741         /**
57742              * @event eventrender
57743              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57744              * @param {Calendar} this
57745              * @param {data} data to be modified
57746              */
57747         'eventrender': true
57748         
57749     });
57750
57751     Roo.grid.Grid.superclass.constructor.call(this);
57752     this.on('render', function() {
57753         this.view.el.addClass('x-grid-cal'); 
57754         
57755         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57756
57757     },this);
57758     
57759     if (!Roo.grid.Calendar.style) {
57760         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57761             
57762             
57763             '.x-grid-cal .x-grid-col' :  {
57764                 height: 'auto !important',
57765                 'vertical-align': 'top'
57766             },
57767             '.x-grid-cal  .fc-event-hori' : {
57768                 height: '14px'
57769             }
57770              
57771             
57772         }, Roo.id());
57773     }
57774
57775     
57776     
57777 };
57778 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57779     /**
57780      * @cfg {Store} eventStore The store that loads events.
57781      */
57782     eventStore : 25,
57783
57784      
57785     activeDate : false,
57786     startDay : 0,
57787     autoWidth : true,
57788     monitorWindowResize : false,
57789
57790     
57791     resizeColumns : function() {
57792         var col = (this.view.el.getWidth() / 7) - 3;
57793         // loop through cols, and setWidth
57794         for(var i =0 ; i < 7 ; i++){
57795             this.cm.setColumnWidth(i, col);
57796         }
57797     },
57798      setDate :function(date) {
57799         
57800         Roo.log('setDate?');
57801         
57802         this.resizeColumns();
57803         var vd = this.activeDate;
57804         this.activeDate = date;
57805 //        if(vd && this.el){
57806 //            var t = date.getTime();
57807 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57808 //                Roo.log('using add remove');
57809 //                
57810 //                this.fireEvent('monthchange', this, date);
57811 //                
57812 //                this.cells.removeClass("fc-state-highlight");
57813 //                this.cells.each(function(c){
57814 //                   if(c.dateValue == t){
57815 //                       c.addClass("fc-state-highlight");
57816 //                       setTimeout(function(){
57817 //                            try{c.dom.firstChild.focus();}catch(e){}
57818 //                       }, 50);
57819 //                       return false;
57820 //                   }
57821 //                   return true;
57822 //                });
57823 //                return;
57824 //            }
57825 //        }
57826         
57827         var days = date.getDaysInMonth();
57828         
57829         var firstOfMonth = date.getFirstDateOfMonth();
57830         var startingPos = firstOfMonth.getDay()-this.startDay;
57831         
57832         if(startingPos < this.startDay){
57833             startingPos += 7;
57834         }
57835         
57836         var pm = date.add(Date.MONTH, -1);
57837         var prevStart = pm.getDaysInMonth()-startingPos;
57838 //        
57839         
57840         
57841         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57842         
57843         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57844         //this.cells.addClassOnOver('fc-state-hover');
57845         
57846         var cells = this.cells.elements;
57847         var textEls = this.textNodes;
57848         
57849         //Roo.each(cells, function(cell){
57850         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57851         //});
57852         
57853         days += startingPos;
57854
57855         // convert everything to numbers so it's fast
57856         var day = 86400000;
57857         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57858         //Roo.log(d);
57859         //Roo.log(pm);
57860         //Roo.log(prevStart);
57861         
57862         var today = new Date().clearTime().getTime();
57863         var sel = date.clearTime().getTime();
57864         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57865         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57866         var ddMatch = this.disabledDatesRE;
57867         var ddText = this.disabledDatesText;
57868         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57869         var ddaysText = this.disabledDaysText;
57870         var format = this.format;
57871         
57872         var setCellClass = function(cal, cell){
57873             
57874             //Roo.log('set Cell Class');
57875             cell.title = "";
57876             var t = d.getTime();
57877             
57878             //Roo.log(d);
57879             
57880             
57881             cell.dateValue = t;
57882             if(t == today){
57883                 cell.className += " fc-today";
57884                 cell.className += " fc-state-highlight";
57885                 cell.title = cal.todayText;
57886             }
57887             if(t == sel){
57888                 // disable highlight in other month..
57889                 cell.className += " fc-state-highlight";
57890                 
57891             }
57892             // disabling
57893             if(t < min) {
57894                 //cell.className = " fc-state-disabled";
57895                 cell.title = cal.minText;
57896                 return;
57897             }
57898             if(t > max) {
57899                 //cell.className = " fc-state-disabled";
57900                 cell.title = cal.maxText;
57901                 return;
57902             }
57903             if(ddays){
57904                 if(ddays.indexOf(d.getDay()) != -1){
57905                     // cell.title = ddaysText;
57906                    // cell.className = " fc-state-disabled";
57907                 }
57908             }
57909             if(ddMatch && format){
57910                 var fvalue = d.dateFormat(format);
57911                 if(ddMatch.test(fvalue)){
57912                     cell.title = ddText.replace("%0", fvalue);
57913                    cell.className = " fc-state-disabled";
57914                 }
57915             }
57916             
57917             if (!cell.initialClassName) {
57918                 cell.initialClassName = cell.dom.className;
57919             }
57920             
57921             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57922         };
57923
57924         var i = 0;
57925         
57926         for(; i < startingPos; i++) {
57927             cells[i].dayName =  (++prevStart);
57928             Roo.log(textEls[i]);
57929             d.setDate(d.getDate()+1);
57930             
57931             //cells[i].className = "fc-past fc-other-month";
57932             setCellClass(this, cells[i]);
57933         }
57934         
57935         var intDay = 0;
57936         
57937         for(; i < days; i++){
57938             intDay = i - startingPos + 1;
57939             cells[i].dayName =  (intDay);
57940             d.setDate(d.getDate()+1);
57941             
57942             cells[i].className = ''; // "x-date-active";
57943             setCellClass(this, cells[i]);
57944         }
57945         var extraDays = 0;
57946         
57947         for(; i < 42; i++) {
57948             //textEls[i].innerHTML = (++extraDays);
57949             
57950             d.setDate(d.getDate()+1);
57951             cells[i].dayName = (++extraDays);
57952             cells[i].className = "fc-future fc-other-month";
57953             setCellClass(this, cells[i]);
57954         }
57955         
57956         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57957         
57958         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57959         
57960         // this will cause all the cells to mis
57961         var rows= [];
57962         var i =0;
57963         for (var r = 0;r < 6;r++) {
57964             for (var c =0;c < 7;c++) {
57965                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57966             }    
57967         }
57968         
57969         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57970         for(i=0;i<cells.length;i++) {
57971             
57972             this.cells.elements[i].dayName = cells[i].dayName ;
57973             this.cells.elements[i].className = cells[i].className;
57974             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57975             this.cells.elements[i].title = cells[i].title ;
57976             this.cells.elements[i].dateValue = cells[i].dateValue ;
57977         }
57978         
57979         
57980         
57981         
57982         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57983         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57984         
57985         ////if(totalRows != 6){
57986             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57987            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57988        // }
57989         
57990         this.fireEvent('monthchange', this, date);
57991         
57992         
57993     },
57994  /**
57995      * Returns the grid's SelectionModel.
57996      * @return {SelectionModel}
57997      */
57998     getSelectionModel : function(){
57999         if(!this.selModel){
58000             this.selModel = new Roo.grid.CellSelectionModel();
58001         }
58002         return this.selModel;
58003     },
58004
58005     load: function() {
58006         this.eventStore.load()
58007         
58008         
58009         
58010     },
58011     
58012     findCell : function(dt) {
58013         dt = dt.clearTime().getTime();
58014         var ret = false;
58015         this.cells.each(function(c){
58016             //Roo.log("check " +c.dateValue + '?=' + dt);
58017             if(c.dateValue == dt){
58018                 ret = c;
58019                 return false;
58020             }
58021             return true;
58022         });
58023         
58024         return ret;
58025     },
58026     
58027     findCells : function(rec) {
58028         var s = rec.data.start_dt.clone().clearTime().getTime();
58029        // Roo.log(s);
58030         var e= rec.data.end_dt.clone().clearTime().getTime();
58031        // Roo.log(e);
58032         var ret = [];
58033         this.cells.each(function(c){
58034              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
58035             
58036             if(c.dateValue > e){
58037                 return ;
58038             }
58039             if(c.dateValue < s){
58040                 return ;
58041             }
58042             ret.push(c);
58043         });
58044         
58045         return ret;    
58046     },
58047     
58048     findBestRow: function(cells)
58049     {
58050         var ret = 0;
58051         
58052         for (var i =0 ; i < cells.length;i++) {
58053             ret  = Math.max(cells[i].rows || 0,ret);
58054         }
58055         return ret;
58056         
58057     },
58058     
58059     
58060     addItem : function(rec)
58061     {
58062         // look for vertical location slot in
58063         var cells = this.findCells(rec);
58064         
58065         rec.row = this.findBestRow(cells);
58066         
58067         // work out the location.
58068         
58069         var crow = false;
58070         var rows = [];
58071         for(var i =0; i < cells.length; i++) {
58072             if (!crow) {
58073                 crow = {
58074                     start : cells[i],
58075                     end :  cells[i]
58076                 };
58077                 continue;
58078             }
58079             if (crow.start.getY() == cells[i].getY()) {
58080                 // on same row.
58081                 crow.end = cells[i];
58082                 continue;
58083             }
58084             // different row.
58085             rows.push(crow);
58086             crow = {
58087                 start: cells[i],
58088                 end : cells[i]
58089             };
58090             
58091         }
58092         
58093         rows.push(crow);
58094         rec.els = [];
58095         rec.rows = rows;
58096         rec.cells = cells;
58097         for (var i = 0; i < cells.length;i++) {
58098             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
58099             
58100         }
58101         
58102         
58103     },
58104     
58105     clearEvents: function() {
58106         
58107         if (!this.eventStore.getCount()) {
58108             return;
58109         }
58110         // reset number of rows in cells.
58111         Roo.each(this.cells.elements, function(c){
58112             c.rows = 0;
58113         });
58114         
58115         this.eventStore.each(function(e) {
58116             this.clearEvent(e);
58117         },this);
58118         
58119     },
58120     
58121     clearEvent : function(ev)
58122     {
58123         if (ev.els) {
58124             Roo.each(ev.els, function(el) {
58125                 el.un('mouseenter' ,this.onEventEnter, this);
58126                 el.un('mouseleave' ,this.onEventLeave, this);
58127                 el.remove();
58128             },this);
58129             ev.els = [];
58130         }
58131     },
58132     
58133     
58134     renderEvent : function(ev,ctr) {
58135         if (!ctr) {
58136              ctr = this.view.el.select('.fc-event-container',true).first();
58137         }
58138         
58139          
58140         this.clearEvent(ev);
58141             //code
58142        
58143         
58144         
58145         ev.els = [];
58146         var cells = ev.cells;
58147         var rows = ev.rows;
58148         this.fireEvent('eventrender', this, ev);
58149         
58150         for(var i =0; i < rows.length; i++) {
58151             
58152             cls = '';
58153             if (i == 0) {
58154                 cls += ' fc-event-start';
58155             }
58156             if ((i+1) == rows.length) {
58157                 cls += ' fc-event-end';
58158             }
58159             
58160             //Roo.log(ev.data);
58161             // how many rows should it span..
58162             var cg = this.eventTmpl.append(ctr,Roo.apply({
58163                 fccls : cls
58164                 
58165             }, ev.data) , true);
58166             
58167             
58168             cg.on('mouseenter' ,this.onEventEnter, this, ev);
58169             cg.on('mouseleave' ,this.onEventLeave, this, ev);
58170             cg.on('click', this.onEventClick, this, ev);
58171             
58172             ev.els.push(cg);
58173             
58174             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
58175             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
58176             //Roo.log(cg);
58177              
58178             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
58179             cg.setWidth(ebox.right - sbox.x -2);
58180         }
58181     },
58182     
58183     renderEvents: function()
58184     {   
58185         // first make sure there is enough space..
58186         
58187         if (!this.eventTmpl) {
58188             this.eventTmpl = new Roo.Template(
58189                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
58190                     '<div class="fc-event-inner">' +
58191                         '<span class="fc-event-time">{time}</span>' +
58192                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
58193                     '</div>' +
58194                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
58195                 '</div>'
58196             );
58197                 
58198         }
58199                
58200         
58201         
58202         this.cells.each(function(c) {
58203             //Roo.log(c.select('.fc-day-content div',true).first());
58204             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58205         });
58206         
58207         var ctr = this.view.el.select('.fc-event-container',true).first();
58208         
58209         var cls;
58210         this.eventStore.each(function(ev){
58211             
58212             this.renderEvent(ev);
58213              
58214              
58215         }, this);
58216         this.view.layout();
58217         
58218     },
58219     
58220     onEventEnter: function (e, el,event,d) {
58221         this.fireEvent('evententer', this, el, event);
58222     },
58223     
58224     onEventLeave: function (e, el,event,d) {
58225         this.fireEvent('eventleave', this, el, event);
58226     },
58227     
58228     onEventClick: function (e, el,event,d) {
58229         this.fireEvent('eventclick', this, el, event);
58230     },
58231     
58232     onMonthChange: function () {
58233         this.store.load();
58234     },
58235     
58236     onLoad: function () {
58237         
58238         //Roo.log('calendar onload');
58239 //         
58240         if(this.eventStore.getCount() > 0){
58241             
58242            
58243             
58244             this.eventStore.each(function(d){
58245                 
58246                 
58247                 // FIXME..
58248                 var add =   d.data;
58249                 if (typeof(add.end_dt) == 'undefined')  {
58250                     Roo.log("Missing End time in calendar data: ");
58251                     Roo.log(d);
58252                     return;
58253                 }
58254                 if (typeof(add.start_dt) == 'undefined')  {
58255                     Roo.log("Missing Start time in calendar data: ");
58256                     Roo.log(d);
58257                     return;
58258                 }
58259                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58260                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58261                 add.id = add.id || d.id;
58262                 add.title = add.title || '??';
58263                 
58264                 this.addItem(d);
58265                 
58266              
58267             },this);
58268         }
58269         
58270         this.renderEvents();
58271     }
58272     
58273
58274 });
58275 /*
58276  grid : {
58277                 xtype: 'Grid',
58278                 xns: Roo.grid,
58279                 listeners : {
58280                     render : function ()
58281                     {
58282                         _this.grid = this;
58283                         
58284                         if (!this.view.el.hasClass('course-timesheet')) {
58285                             this.view.el.addClass('course-timesheet');
58286                         }
58287                         if (this.tsStyle) {
58288                             this.ds.load({});
58289                             return; 
58290                         }
58291                         Roo.log('width');
58292                         Roo.log(_this.grid.view.el.getWidth());
58293                         
58294                         
58295                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58296                             '.course-timesheet .x-grid-row' : {
58297                                 height: '80px'
58298                             },
58299                             '.x-grid-row td' : {
58300                                 'vertical-align' : 0
58301                             },
58302                             '.course-edit-link' : {
58303                                 'color' : 'blue',
58304                                 'text-overflow' : 'ellipsis',
58305                                 'overflow' : 'hidden',
58306                                 'white-space' : 'nowrap',
58307                                 'cursor' : 'pointer'
58308                             },
58309                             '.sub-link' : {
58310                                 'color' : 'green'
58311                             },
58312                             '.de-act-sup-link' : {
58313                                 'color' : 'purple',
58314                                 'text-decoration' : 'line-through'
58315                             },
58316                             '.de-act-link' : {
58317                                 'color' : 'red',
58318                                 'text-decoration' : 'line-through'
58319                             },
58320                             '.course-timesheet .course-highlight' : {
58321                                 'border-top-style': 'dashed !important',
58322                                 'border-bottom-bottom': 'dashed !important'
58323                             },
58324                             '.course-timesheet .course-item' : {
58325                                 'font-family'   : 'tahoma, arial, helvetica',
58326                                 'font-size'     : '11px',
58327                                 'overflow'      : 'hidden',
58328                                 'padding-left'  : '10px',
58329                                 'padding-right' : '10px',
58330                                 'padding-top' : '10px' 
58331                             }
58332                             
58333                         }, Roo.id());
58334                                 this.ds.load({});
58335                     }
58336                 },
58337                 autoWidth : true,
58338                 monitorWindowResize : false,
58339                 cellrenderer : function(v,x,r)
58340                 {
58341                     return v;
58342                 },
58343                 sm : {
58344                     xtype: 'CellSelectionModel',
58345                     xns: Roo.grid
58346                 },
58347                 dataSource : {
58348                     xtype: 'Store',
58349                     xns: Roo.data,
58350                     listeners : {
58351                         beforeload : function (_self, options)
58352                         {
58353                             options.params = options.params || {};
58354                             options.params._month = _this.monthField.getValue();
58355                             options.params.limit = 9999;
58356                             options.params['sort'] = 'when_dt';    
58357                             options.params['dir'] = 'ASC';    
58358                             this.proxy.loadResponse = this.loadResponse;
58359                             Roo.log("load?");
58360                             //this.addColumns();
58361                         },
58362                         load : function (_self, records, options)
58363                         {
58364                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58365                                 // if you click on the translation.. you can edit it...
58366                                 var el = Roo.get(this);
58367                                 var id = el.dom.getAttribute('data-id');
58368                                 var d = el.dom.getAttribute('data-date');
58369                                 var t = el.dom.getAttribute('data-time');
58370                                 //var id = this.child('span').dom.textContent;
58371                                 
58372                                 //Roo.log(this);
58373                                 Pman.Dialog.CourseCalendar.show({
58374                                     id : id,
58375                                     when_d : d,
58376                                     when_t : t,
58377                                     productitem_active : id ? 1 : 0
58378                                 }, function() {
58379                                     _this.grid.ds.load({});
58380                                 });
58381                            
58382                            });
58383                            
58384                            _this.panel.fireEvent('resize', [ '', '' ]);
58385                         }
58386                     },
58387                     loadResponse : function(o, success, response){
58388                             // this is overridden on before load..
58389                             
58390                             Roo.log("our code?");       
58391                             //Roo.log(success);
58392                             //Roo.log(response)
58393                             delete this.activeRequest;
58394                             if(!success){
58395                                 this.fireEvent("loadexception", this, o, response);
58396                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58397                                 return;
58398                             }
58399                             var result;
58400                             try {
58401                                 result = o.reader.read(response);
58402                             }catch(e){
58403                                 Roo.log("load exception?");
58404                                 this.fireEvent("loadexception", this, o, response, e);
58405                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58406                                 return;
58407                             }
58408                             Roo.log("ready...");        
58409                             // loop through result.records;
58410                             // and set this.tdate[date] = [] << array of records..
58411                             _this.tdata  = {};
58412                             Roo.each(result.records, function(r){
58413                                 //Roo.log(r.data);
58414                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58415                                     _this.tdata[r.data.when_dt.format('j')] = [];
58416                                 }
58417                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58418                             });
58419                             
58420                             //Roo.log(_this.tdata);
58421                             
58422                             result.records = [];
58423                             result.totalRecords = 6;
58424                     
58425                             // let's generate some duumy records for the rows.
58426                             //var st = _this.dateField.getValue();
58427                             
58428                             // work out monday..
58429                             //st = st.add(Date.DAY, -1 * st.format('w'));
58430                             
58431                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58432                             
58433                             var firstOfMonth = date.getFirstDayOfMonth();
58434                             var days = date.getDaysInMonth();
58435                             var d = 1;
58436                             var firstAdded = false;
58437                             for (var i = 0; i < result.totalRecords ; i++) {
58438                                 //var d= st.add(Date.DAY, i);
58439                                 var row = {};
58440                                 var added = 0;
58441                                 for(var w = 0 ; w < 7 ; w++){
58442                                     if(!firstAdded && firstOfMonth != w){
58443                                         continue;
58444                                     }
58445                                     if(d > days){
58446                                         continue;
58447                                     }
58448                                     firstAdded = true;
58449                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58450                                     row['weekday'+w] = String.format(
58451                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58452                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58453                                                     d,
58454                                                     date.format('Y-m-')+dd
58455                                                 );
58456                                     added++;
58457                                     if(typeof(_this.tdata[d]) != 'undefined'){
58458                                         Roo.each(_this.tdata[d], function(r){
58459                                             var is_sub = '';
58460                                             var deactive = '';
58461                                             var id = r.id;
58462                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58463                                             if(r.parent_id*1>0){
58464                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58465                                                 id = r.parent_id;
58466                                             }
58467                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58468                                                 deactive = 'de-act-link';
58469                                             }
58470                                             
58471                                             row['weekday'+w] += String.format(
58472                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58473                                                     id, //0
58474                                                     r.product_id_name, //1
58475                                                     r.when_dt.format('h:ia'), //2
58476                                                     is_sub, //3
58477                                                     deactive, //4
58478                                                     desc // 5
58479                                             );
58480                                         });
58481                                     }
58482                                     d++;
58483                                 }
58484                                 
58485                                 // only do this if something added..
58486                                 if(added > 0){ 
58487                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58488                                 }
58489                                 
58490                                 
58491                                 // push it twice. (second one with an hour..
58492                                 
58493                             }
58494                             //Roo.log(result);
58495                             this.fireEvent("load", this, o, o.request.arg);
58496                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58497                         },
58498                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58499                     proxy : {
58500                         xtype: 'HttpProxy',
58501                         xns: Roo.data,
58502                         method : 'GET',
58503                         url : baseURL + '/Roo/Shop_course.php'
58504                     },
58505                     reader : {
58506                         xtype: 'JsonReader',
58507                         xns: Roo.data,
58508                         id : 'id',
58509                         fields : [
58510                             {
58511                                 'name': 'id',
58512                                 'type': 'int'
58513                             },
58514                             {
58515                                 'name': 'when_dt',
58516                                 'type': 'string'
58517                             },
58518                             {
58519                                 'name': 'end_dt',
58520                                 'type': 'string'
58521                             },
58522                             {
58523                                 'name': 'parent_id',
58524                                 'type': 'int'
58525                             },
58526                             {
58527                                 'name': 'product_id',
58528                                 'type': 'int'
58529                             },
58530                             {
58531                                 'name': 'productitem_id',
58532                                 'type': 'int'
58533                             },
58534                             {
58535                                 'name': 'guid',
58536                                 'type': 'int'
58537                             }
58538                         ]
58539                     }
58540                 },
58541                 toolbar : {
58542                     xtype: 'Toolbar',
58543                     xns: Roo,
58544                     items : [
58545                         {
58546                             xtype: 'Button',
58547                             xns: Roo.Toolbar,
58548                             listeners : {
58549                                 click : function (_self, e)
58550                                 {
58551                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58552                                     sd.setMonth(sd.getMonth()-1);
58553                                     _this.monthField.setValue(sd.format('Y-m-d'));
58554                                     _this.grid.ds.load({});
58555                                 }
58556                             },
58557                             text : "Back"
58558                         },
58559                         {
58560                             xtype: 'Separator',
58561                             xns: Roo.Toolbar
58562                         },
58563                         {
58564                             xtype: 'MonthField',
58565                             xns: Roo.form,
58566                             listeners : {
58567                                 render : function (_self)
58568                                 {
58569                                     _this.monthField = _self;
58570                                    // _this.monthField.set  today
58571                                 },
58572                                 select : function (combo, date)
58573                                 {
58574                                     _this.grid.ds.load({});
58575                                 }
58576                             },
58577                             value : (function() { return new Date(); })()
58578                         },
58579                         {
58580                             xtype: 'Separator',
58581                             xns: Roo.Toolbar
58582                         },
58583                         {
58584                             xtype: 'TextItem',
58585                             xns: Roo.Toolbar,
58586                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58587                         },
58588                         {
58589                             xtype: 'Fill',
58590                             xns: Roo.Toolbar
58591                         },
58592                         {
58593                             xtype: 'Button',
58594                             xns: Roo.Toolbar,
58595                             listeners : {
58596                                 click : function (_self, e)
58597                                 {
58598                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58599                                     sd.setMonth(sd.getMonth()+1);
58600                                     _this.monthField.setValue(sd.format('Y-m-d'));
58601                                     _this.grid.ds.load({});
58602                                 }
58603                             },
58604                             text : "Next"
58605                         }
58606                     ]
58607                 },
58608                  
58609             }
58610         };
58611         
58612         *//*
58613  * Based on:
58614  * Ext JS Library 1.1.1
58615  * Copyright(c) 2006-2007, Ext JS, LLC.
58616  *
58617  * Originally Released Under LGPL - original licence link has changed is not relivant.
58618  *
58619  * Fork - LGPL
58620  * <script type="text/javascript">
58621  */
58622  
58623 /**
58624  * @class Roo.LoadMask
58625  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58626  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58627  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58628  * element's UpdateManager load indicator and will be destroyed after the initial load.
58629  * @constructor
58630  * Create a new LoadMask
58631  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58632  * @param {Object} config The config object
58633  */
58634 Roo.LoadMask = function(el, config){
58635     this.el = Roo.get(el);
58636     Roo.apply(this, config);
58637     if(this.store){
58638         this.store.on('beforeload', this.onBeforeLoad, this);
58639         this.store.on('load', this.onLoad, this);
58640         this.store.on('loadexception', this.onLoadException, this);
58641         this.removeMask = false;
58642     }else{
58643         var um = this.el.getUpdateManager();
58644         um.showLoadIndicator = false; // disable the default indicator
58645         um.on('beforeupdate', this.onBeforeLoad, this);
58646         um.on('update', this.onLoad, this);
58647         um.on('failure', this.onLoad, this);
58648         this.removeMask = true;
58649     }
58650 };
58651
58652 Roo.LoadMask.prototype = {
58653     /**
58654      * @cfg {Boolean} removeMask
58655      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58656      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58657      */
58658     /**
58659      * @cfg {String} msg
58660      * The text to display in a centered loading message box (defaults to 'Loading...')
58661      */
58662     msg : 'Loading...',
58663     /**
58664      * @cfg {String} msgCls
58665      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58666      */
58667     msgCls : 'x-mask-loading',
58668
58669     /**
58670      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58671      * @type Boolean
58672      */
58673     disabled: false,
58674
58675     /**
58676      * Disables the mask to prevent it from being displayed
58677      */
58678     disable : function(){
58679        this.disabled = true;
58680     },
58681
58682     /**
58683      * Enables the mask so that it can be displayed
58684      */
58685     enable : function(){
58686         this.disabled = false;
58687     },
58688     
58689     onLoadException : function()
58690     {
58691         Roo.log(arguments);
58692         
58693         if (typeof(arguments[3]) != 'undefined') {
58694             Roo.MessageBox.alert("Error loading",arguments[3]);
58695         } 
58696         /*
58697         try {
58698             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58699                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58700             }   
58701         } catch(e) {
58702             
58703         }
58704         */
58705     
58706         
58707         
58708         this.el.unmask(this.removeMask);
58709     },
58710     // private
58711     onLoad : function()
58712     {
58713         this.el.unmask(this.removeMask);
58714     },
58715
58716     // private
58717     onBeforeLoad : function(){
58718         if(!this.disabled){
58719             this.el.mask(this.msg, this.msgCls);
58720         }
58721     },
58722
58723     // private
58724     destroy : function(){
58725         if(this.store){
58726             this.store.un('beforeload', this.onBeforeLoad, this);
58727             this.store.un('load', this.onLoad, this);
58728             this.store.un('loadexception', this.onLoadException, this);
58729         }else{
58730             var um = this.el.getUpdateManager();
58731             um.un('beforeupdate', this.onBeforeLoad, this);
58732             um.un('update', this.onLoad, this);
58733             um.un('failure', this.onLoad, this);
58734         }
58735     }
58736 };/*
58737  * Based on:
58738  * Ext JS Library 1.1.1
58739  * Copyright(c) 2006-2007, Ext JS, LLC.
58740  *
58741  * Originally Released Under LGPL - original licence link has changed is not relivant.
58742  *
58743  * Fork - LGPL
58744  * <script type="text/javascript">
58745  */
58746
58747
58748 /**
58749  * @class Roo.XTemplate
58750  * @extends Roo.Template
58751  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58752 <pre><code>
58753 var t = new Roo.XTemplate(
58754         '&lt;select name="{name}"&gt;',
58755                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58756         '&lt;/select&gt;'
58757 );
58758  
58759 // then append, applying the master template values
58760  </code></pre>
58761  *
58762  * Supported features:
58763  *
58764  *  Tags:
58765
58766 <pre><code>
58767       {a_variable} - output encoded.
58768       {a_variable.format:("Y-m-d")} - call a method on the variable
58769       {a_variable:raw} - unencoded output
58770       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58771       {a_variable:this.method_on_template(...)} - call a method on the template object.
58772  
58773 </code></pre>
58774  *  The tpl tag:
58775 <pre><code>
58776         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58777         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58778         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58779         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58780   
58781         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58782         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58783 </code></pre>
58784  *      
58785  */
58786 Roo.XTemplate = function()
58787 {
58788     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58789     if (this.html) {
58790         this.compile();
58791     }
58792 };
58793
58794
58795 Roo.extend(Roo.XTemplate, Roo.Template, {
58796
58797     /**
58798      * The various sub templates
58799      */
58800     tpls : false,
58801     /**
58802      *
58803      * basic tag replacing syntax
58804      * WORD:WORD()
58805      *
58806      * // you can fake an object call by doing this
58807      *  x.t:(test,tesT) 
58808      * 
58809      */
58810     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58811
58812     /**
58813      * compile the template
58814      *
58815      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58816      *
58817      */
58818     compile: function()
58819     {
58820         var s = this.html;
58821      
58822         s = ['<tpl>', s, '</tpl>'].join('');
58823     
58824         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58825             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58826             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58827             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58828             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58829             m,
58830             id     = 0,
58831             tpls   = [];
58832     
58833         while(true == !!(m = s.match(re))){
58834             var forMatch   = m[0].match(nameRe),
58835                 ifMatch   = m[0].match(ifRe),
58836                 execMatch   = m[0].match(execRe),
58837                 namedMatch   = m[0].match(namedRe),
58838                 
58839                 exp  = null, 
58840                 fn   = null,
58841                 exec = null,
58842                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58843                 
58844             if (ifMatch) {
58845                 // if - puts fn into test..
58846                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58847                 if(exp){
58848                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58849                 }
58850             }
58851             
58852             if (execMatch) {
58853                 // exec - calls a function... returns empty if true is  returned.
58854                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58855                 if(exp){
58856                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58857                 }
58858             }
58859             
58860             
58861             if (name) {
58862                 // for = 
58863                 switch(name){
58864                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58865                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58866                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58867                 }
58868             }
58869             var uid = namedMatch ? namedMatch[1] : id;
58870             
58871             
58872             tpls.push({
58873                 id:     namedMatch ? namedMatch[1] : id,
58874                 target: name,
58875                 exec:   exec,
58876                 test:   fn,
58877                 body:   m[1] || ''
58878             });
58879             if (namedMatch) {
58880                 s = s.replace(m[0], '');
58881             } else { 
58882                 s = s.replace(m[0], '{xtpl'+ id + '}');
58883             }
58884             ++id;
58885         }
58886         this.tpls = [];
58887         for(var i = tpls.length-1; i >= 0; --i){
58888             this.compileTpl(tpls[i]);
58889             this.tpls[tpls[i].id] = tpls[i];
58890         }
58891         this.master = tpls[tpls.length-1];
58892         return this;
58893     },
58894     /**
58895      * same as applyTemplate, except it's done to one of the subTemplates
58896      * when using named templates, you can do:
58897      *
58898      * var str = pl.applySubTemplate('your-name', values);
58899      *
58900      * 
58901      * @param {Number} id of the template
58902      * @param {Object} values to apply to template
58903      * @param {Object} parent (normaly the instance of this object)
58904      */
58905     applySubTemplate : function(id, values, parent)
58906     {
58907         
58908         
58909         var t = this.tpls[id];
58910         
58911         
58912         try { 
58913             if(t.test && !t.test.call(this, values, parent)){
58914                 return '';
58915             }
58916         } catch(e) {
58917             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58918             Roo.log(e.toString());
58919             Roo.log(t.test);
58920             return ''
58921         }
58922         try { 
58923             
58924             if(t.exec && t.exec.call(this, values, parent)){
58925                 return '';
58926             }
58927         } catch(e) {
58928             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58929             Roo.log(e.toString());
58930             Roo.log(t.exec);
58931             return ''
58932         }
58933         try {
58934             var vs = t.target ? t.target.call(this, values, parent) : values;
58935             parent = t.target ? values : parent;
58936             if(t.target && vs instanceof Array){
58937                 var buf = [];
58938                 for(var i = 0, len = vs.length; i < len; i++){
58939                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58940                 }
58941                 return buf.join('');
58942             }
58943             return t.compiled.call(this, vs, parent);
58944         } catch (e) {
58945             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58946             Roo.log(e.toString());
58947             Roo.log(t.compiled);
58948             return '';
58949         }
58950     },
58951
58952     compileTpl : function(tpl)
58953     {
58954         var fm = Roo.util.Format;
58955         var useF = this.disableFormats !== true;
58956         var sep = Roo.isGecko ? "+" : ",";
58957         var undef = function(str) {
58958             Roo.log("Property not found :"  + str);
58959             return '';
58960         };
58961         
58962         var fn = function(m, name, format, args)
58963         {
58964             //Roo.log(arguments);
58965             args = args ? args.replace(/\\'/g,"'") : args;
58966             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58967             if (typeof(format) == 'undefined') {
58968                 format= 'htmlEncode';
58969             }
58970             if (format == 'raw' ) {
58971                 format = false;
58972             }
58973             
58974             if(name.substr(0, 4) == 'xtpl'){
58975                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58976             }
58977             
58978             // build an array of options to determine if value is undefined..
58979             
58980             // basically get 'xxxx.yyyy' then do
58981             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58982             //    (function () { Roo.log("Property not found"); return ''; })() :
58983             //    ......
58984             
58985             var udef_ar = [];
58986             var lookfor = '';
58987             Roo.each(name.split('.'), function(st) {
58988                 lookfor += (lookfor.length ? '.': '') + st;
58989                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58990             });
58991             
58992             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58993             
58994             
58995             if(format && useF){
58996                 
58997                 args = args ? ',' + args : "";
58998                  
58999                 if(format.substr(0, 5) != "this."){
59000                     format = "fm." + format + '(';
59001                 }else{
59002                     format = 'this.call("'+ format.substr(5) + '", ';
59003                     args = ", values";
59004                 }
59005                 
59006                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
59007             }
59008              
59009             if (args.length) {
59010                 // called with xxyx.yuu:(test,test)
59011                 // change to ()
59012                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
59013             }
59014             // raw.. - :raw modifier..
59015             return "'"+ sep + udef_st  + name + ")"+sep+"'";
59016             
59017         };
59018         var body;
59019         // branched to use + in gecko and [].join() in others
59020         if(Roo.isGecko){
59021             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
59022                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
59023                     "';};};";
59024         }else{
59025             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
59026             body.push(tpl.body.replace(/(\r\n|\n)/g,
59027                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
59028             body.push("'].join('');};};");
59029             body = body.join('');
59030         }
59031         
59032         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
59033        
59034         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
59035         eval(body);
59036         
59037         return this;
59038     },
59039
59040     applyTemplate : function(values){
59041         return this.master.compiled.call(this, values, {});
59042         //var s = this.subs;
59043     },
59044
59045     apply : function(){
59046         return this.applyTemplate.apply(this, arguments);
59047     }
59048
59049  });
59050
59051 Roo.XTemplate.from = function(el){
59052     el = Roo.getDom(el);
59053     return new Roo.XTemplate(el.value || el.innerHTML);
59054 };