roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4882 <p>
4883 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4884
4885 <p>
4886 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * 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  /**
16491  * marked - a markdown parser
16492  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16493  * https://github.com/chjj/marked
16494  */
16495
16496
16497 /**
16498  *
16499  * Roo.Markdown - is a very crude wrapper around marked..
16500  *
16501  * usage:
16502  * 
16503  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16504  * 
16505  * Note: move the sample code to the bottom of this
16506  * file before uncommenting it.
16507  *
16508  */
16509
16510 Roo.Markdown = {};
16511 Roo.Markdown.toHtml = function(text) {
16512     
16513     var c = new Roo.Markdown.marked.setOptions({
16514             renderer: new Roo.Markdown.marked.Renderer(),
16515             gfm: true,
16516             tables: true,
16517             breaks: false,
16518             pedantic: false,
16519             sanitize: false,
16520             smartLists: true,
16521             smartypants: false
16522           });
16523
16524     return Roo.Markdown.marked(text);
16525 };
16526 //
16527 // converter
16528 //
16529 // Wraps all "globals" so that the only thing
16530 // exposed is makeHtml().
16531 //
16532 (function() {
16533     
16534     /**
16535      * Block-Level Grammar
16536      */
16537     
16538     var block = {
16539       newline: /^\n+/,
16540       code: /^( {4}[^\n]+\n*)+/,
16541       fences: noop,
16542       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16543       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16544       nptable: noop,
16545       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16546       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16547       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16548       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16549       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16550       table: noop,
16551       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16552       text: /^[^\n]+/
16553     };
16554     
16555     block.bullet = /(?:[*+-]|\d+\.)/;
16556     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16557     block.item = replace(block.item, 'gm')
16558       (/bull/g, block.bullet)
16559       ();
16560     
16561     block.list = replace(block.list)
16562       (/bull/g, block.bullet)
16563       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16564       ('def', '\\n+(?=' + block.def.source + ')')
16565       ();
16566     
16567     block.blockquote = replace(block.blockquote)
16568       ('def', block.def)
16569       ();
16570     
16571     block._tag = '(?!(?:'
16572       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16573       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16574       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16575     
16576     block.html = replace(block.html)
16577       ('comment', /<!--[\s\S]*?-->/)
16578       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16579       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16580       (/tag/g, block._tag)
16581       ();
16582     
16583     block.paragraph = replace(block.paragraph)
16584       ('hr', block.hr)
16585       ('heading', block.heading)
16586       ('lheading', block.lheading)
16587       ('blockquote', block.blockquote)
16588       ('tag', '<' + block._tag)
16589       ('def', block.def)
16590       ();
16591     
16592     /**
16593      * Normal Block Grammar
16594      */
16595     
16596     block.normal = merge({}, block);
16597     
16598     /**
16599      * GFM Block Grammar
16600      */
16601     
16602     block.gfm = merge({}, block.normal, {
16603       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16604       paragraph: /^/,
16605       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16606     });
16607     
16608     block.gfm.paragraph = replace(block.paragraph)
16609       ('(?!', '(?!'
16610         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16611         + block.list.source.replace('\\1', '\\3') + '|')
16612       ();
16613     
16614     /**
16615      * GFM + Tables Block Grammar
16616      */
16617     
16618     block.tables = merge({}, block.gfm, {
16619       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16620       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16621     });
16622     
16623     /**
16624      * Block Lexer
16625      */
16626     
16627     function Lexer(options) {
16628       this.tokens = [];
16629       this.tokens.links = {};
16630       this.options = options || marked.defaults;
16631       this.rules = block.normal;
16632     
16633       if (this.options.gfm) {
16634         if (this.options.tables) {
16635           this.rules = block.tables;
16636         } else {
16637           this.rules = block.gfm;
16638         }
16639       }
16640     }
16641     
16642     /**
16643      * Expose Block Rules
16644      */
16645     
16646     Lexer.rules = block;
16647     
16648     /**
16649      * Static Lex Method
16650      */
16651     
16652     Lexer.lex = function(src, options) {
16653       var lexer = new Lexer(options);
16654       return lexer.lex(src);
16655     };
16656     
16657     /**
16658      * Preprocessing
16659      */
16660     
16661     Lexer.prototype.lex = function(src) {
16662       src = src
16663         .replace(/\r\n|\r/g, '\n')
16664         .replace(/\t/g, '    ')
16665         .replace(/\u00a0/g, ' ')
16666         .replace(/\u2424/g, '\n');
16667     
16668       return this.token(src, true);
16669     };
16670     
16671     /**
16672      * Lexing
16673      */
16674     
16675     Lexer.prototype.token = function(src, top, bq) {
16676       var src = src.replace(/^ +$/gm, '')
16677         , next
16678         , loose
16679         , cap
16680         , bull
16681         , b
16682         , item
16683         , space
16684         , i
16685         , l;
16686     
16687       while (src) {
16688         // newline
16689         if (cap = this.rules.newline.exec(src)) {
16690           src = src.substring(cap[0].length);
16691           if (cap[0].length > 1) {
16692             this.tokens.push({
16693               type: 'space'
16694             });
16695           }
16696         }
16697     
16698         // code
16699         if (cap = this.rules.code.exec(src)) {
16700           src = src.substring(cap[0].length);
16701           cap = cap[0].replace(/^ {4}/gm, '');
16702           this.tokens.push({
16703             type: 'code',
16704             text: !this.options.pedantic
16705               ? cap.replace(/\n+$/, '')
16706               : cap
16707           });
16708           continue;
16709         }
16710     
16711         // fences (gfm)
16712         if (cap = this.rules.fences.exec(src)) {
16713           src = src.substring(cap[0].length);
16714           this.tokens.push({
16715             type: 'code',
16716             lang: cap[2],
16717             text: cap[3] || ''
16718           });
16719           continue;
16720         }
16721     
16722         // heading
16723         if (cap = this.rules.heading.exec(src)) {
16724           src = src.substring(cap[0].length);
16725           this.tokens.push({
16726             type: 'heading',
16727             depth: cap[1].length,
16728             text: cap[2]
16729           });
16730           continue;
16731         }
16732     
16733         // table no leading pipe (gfm)
16734         if (top && (cap = this.rules.nptable.exec(src))) {
16735           src = src.substring(cap[0].length);
16736     
16737           item = {
16738             type: 'table',
16739             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16740             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16741             cells: cap[3].replace(/\n$/, '').split('\n')
16742           };
16743     
16744           for (i = 0; i < item.align.length; i++) {
16745             if (/^ *-+: *$/.test(item.align[i])) {
16746               item.align[i] = 'right';
16747             } else if (/^ *:-+: *$/.test(item.align[i])) {
16748               item.align[i] = 'center';
16749             } else if (/^ *:-+ *$/.test(item.align[i])) {
16750               item.align[i] = 'left';
16751             } else {
16752               item.align[i] = null;
16753             }
16754           }
16755     
16756           for (i = 0; i < item.cells.length; i++) {
16757             item.cells[i] = item.cells[i].split(/ *\| */);
16758           }
16759     
16760           this.tokens.push(item);
16761     
16762           continue;
16763         }
16764     
16765         // lheading
16766         if (cap = this.rules.lheading.exec(src)) {
16767           src = src.substring(cap[0].length);
16768           this.tokens.push({
16769             type: 'heading',
16770             depth: cap[2] === '=' ? 1 : 2,
16771             text: cap[1]
16772           });
16773           continue;
16774         }
16775     
16776         // hr
16777         if (cap = this.rules.hr.exec(src)) {
16778           src = src.substring(cap[0].length);
16779           this.tokens.push({
16780             type: 'hr'
16781           });
16782           continue;
16783         }
16784     
16785         // blockquote
16786         if (cap = this.rules.blockquote.exec(src)) {
16787           src = src.substring(cap[0].length);
16788     
16789           this.tokens.push({
16790             type: 'blockquote_start'
16791           });
16792     
16793           cap = cap[0].replace(/^ *> ?/gm, '');
16794     
16795           // Pass `top` to keep the current
16796           // "toplevel" state. This is exactly
16797           // how markdown.pl works.
16798           this.token(cap, top, true);
16799     
16800           this.tokens.push({
16801             type: 'blockquote_end'
16802           });
16803     
16804           continue;
16805         }
16806     
16807         // list
16808         if (cap = this.rules.list.exec(src)) {
16809           src = src.substring(cap[0].length);
16810           bull = cap[2];
16811     
16812           this.tokens.push({
16813             type: 'list_start',
16814             ordered: bull.length > 1
16815           });
16816     
16817           // Get each top-level item.
16818           cap = cap[0].match(this.rules.item);
16819     
16820           next = false;
16821           l = cap.length;
16822           i = 0;
16823     
16824           for (; i < l; i++) {
16825             item = cap[i];
16826     
16827             // Remove the list item's bullet
16828             // so it is seen as the next token.
16829             space = item.length;
16830             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16831     
16832             // Outdent whatever the
16833             // list item contains. Hacky.
16834             if (~item.indexOf('\n ')) {
16835               space -= item.length;
16836               item = !this.options.pedantic
16837                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16838                 : item.replace(/^ {1,4}/gm, '');
16839             }
16840     
16841             // Determine whether the next list item belongs here.
16842             // Backpedal if it does not belong in this list.
16843             if (this.options.smartLists && i !== l - 1) {
16844               b = block.bullet.exec(cap[i + 1])[0];
16845               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16846                 src = cap.slice(i + 1).join('\n') + src;
16847                 i = l - 1;
16848               }
16849             }
16850     
16851             // Determine whether item is loose or not.
16852             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16853             // for discount behavior.
16854             loose = next || /\n\n(?!\s*$)/.test(item);
16855             if (i !== l - 1) {
16856               next = item.charAt(item.length - 1) === '\n';
16857               if (!loose) { loose = next; }
16858             }
16859     
16860             this.tokens.push({
16861               type: loose
16862                 ? 'loose_item_start'
16863                 : 'list_item_start'
16864             });
16865     
16866             // Recurse.
16867             this.token(item, false, bq);
16868     
16869             this.tokens.push({
16870               type: 'list_item_end'
16871             });
16872           }
16873     
16874           this.tokens.push({
16875             type: 'list_end'
16876           });
16877     
16878           continue;
16879         }
16880     
16881         // html
16882         if (cap = this.rules.html.exec(src)) {
16883           src = src.substring(cap[0].length);
16884           this.tokens.push({
16885             type: this.options.sanitize
16886               ? 'paragraph'
16887               : 'html',
16888             pre: !this.options.sanitizer
16889               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
16890             text: cap[0]
16891           });
16892           continue;
16893         }
16894     
16895         // def
16896         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
16897           src = src.substring(cap[0].length);
16898           this.tokens.links[cap[1].toLowerCase()] = {
16899             href: cap[2],
16900             title: cap[3]
16901           };
16902           continue;
16903         }
16904     
16905         // table (gfm)
16906         if (top && (cap = this.rules.table.exec(src))) {
16907           src = src.substring(cap[0].length);
16908     
16909           item = {
16910             type: 'table',
16911             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16912             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16913             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
16914           };
16915     
16916           for (i = 0; i < item.align.length; i++) {
16917             if (/^ *-+: *$/.test(item.align[i])) {
16918               item.align[i] = 'right';
16919             } else if (/^ *:-+: *$/.test(item.align[i])) {
16920               item.align[i] = 'center';
16921             } else if (/^ *:-+ *$/.test(item.align[i])) {
16922               item.align[i] = 'left';
16923             } else {
16924               item.align[i] = null;
16925             }
16926           }
16927     
16928           for (i = 0; i < item.cells.length; i++) {
16929             item.cells[i] = item.cells[i]
16930               .replace(/^ *\| *| *\| *$/g, '')
16931               .split(/ *\| */);
16932           }
16933     
16934           this.tokens.push(item);
16935     
16936           continue;
16937         }
16938     
16939         // top-level paragraph
16940         if (top && (cap = this.rules.paragraph.exec(src))) {
16941           src = src.substring(cap[0].length);
16942           this.tokens.push({
16943             type: 'paragraph',
16944             text: cap[1].charAt(cap[1].length - 1) === '\n'
16945               ? cap[1].slice(0, -1)
16946               : cap[1]
16947           });
16948           continue;
16949         }
16950     
16951         // text
16952         if (cap = this.rules.text.exec(src)) {
16953           // Top-level should never reach here.
16954           src = src.substring(cap[0].length);
16955           this.tokens.push({
16956             type: 'text',
16957             text: cap[0]
16958           });
16959           continue;
16960         }
16961     
16962         if (src) {
16963           throw new
16964             Error('Infinite loop on byte: ' + src.charCodeAt(0));
16965         }
16966       }
16967     
16968       return this.tokens;
16969     };
16970     
16971     /**
16972      * Inline-Level Grammar
16973      */
16974     
16975     var inline = {
16976       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
16977       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
16978       url: noop,
16979       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
16980       link: /^!?\[(inside)\]\(href\)/,
16981       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
16982       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
16983       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
16984       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
16985       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
16986       br: /^ {2,}\n(?!\s*$)/,
16987       del: noop,
16988       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
16989     };
16990     
16991     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
16992     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
16993     
16994     inline.link = replace(inline.link)
16995       ('inside', inline._inside)
16996       ('href', inline._href)
16997       ();
16998     
16999     inline.reflink = replace(inline.reflink)
17000       ('inside', inline._inside)
17001       ();
17002     
17003     /**
17004      * Normal Inline Grammar
17005      */
17006     
17007     inline.normal = merge({}, inline);
17008     
17009     /**
17010      * Pedantic Inline Grammar
17011      */
17012     
17013     inline.pedantic = merge({}, inline.normal, {
17014       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17015       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17016     });
17017     
17018     /**
17019      * GFM Inline Grammar
17020      */
17021     
17022     inline.gfm = merge({}, inline.normal, {
17023       escape: replace(inline.escape)('])', '~|])')(),
17024       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17025       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17026       text: replace(inline.text)
17027         (']|', '~]|')
17028         ('|', '|https?://|')
17029         ()
17030     });
17031     
17032     /**
17033      * GFM + Line Breaks Inline Grammar
17034      */
17035     
17036     inline.breaks = merge({}, inline.gfm, {
17037       br: replace(inline.br)('{2,}', '*')(),
17038       text: replace(inline.gfm.text)('{2,}', '*')()
17039     });
17040     
17041     /**
17042      * Inline Lexer & Compiler
17043      */
17044     
17045     function InlineLexer(links, options) {
17046       this.options = options || marked.defaults;
17047       this.links = links;
17048       this.rules = inline.normal;
17049       this.renderer = this.options.renderer || new Renderer;
17050       this.renderer.options = this.options;
17051     
17052       if (!this.links) {
17053         throw new
17054           Error('Tokens array requires a `links` property.');
17055       }
17056     
17057       if (this.options.gfm) {
17058         if (this.options.breaks) {
17059           this.rules = inline.breaks;
17060         } else {
17061           this.rules = inline.gfm;
17062         }
17063       } else if (this.options.pedantic) {
17064         this.rules = inline.pedantic;
17065       }
17066     }
17067     
17068     /**
17069      * Expose Inline Rules
17070      */
17071     
17072     InlineLexer.rules = inline;
17073     
17074     /**
17075      * Static Lexing/Compiling Method
17076      */
17077     
17078     InlineLexer.output = function(src, links, options) {
17079       var inline = new InlineLexer(links, options);
17080       return inline.output(src);
17081     };
17082     
17083     /**
17084      * Lexing/Compiling
17085      */
17086     
17087     InlineLexer.prototype.output = function(src) {
17088       var out = ''
17089         , link
17090         , text
17091         , href
17092         , cap;
17093     
17094       while (src) {
17095         // escape
17096         if (cap = this.rules.escape.exec(src)) {
17097           src = src.substring(cap[0].length);
17098           out += cap[1];
17099           continue;
17100         }
17101     
17102         // autolink
17103         if (cap = this.rules.autolink.exec(src)) {
17104           src = src.substring(cap[0].length);
17105           if (cap[2] === '@') {
17106             text = cap[1].charAt(6) === ':'
17107               ? this.mangle(cap[1].substring(7))
17108               : this.mangle(cap[1]);
17109             href = this.mangle('mailto:') + text;
17110           } else {
17111             text = escape(cap[1]);
17112             href = text;
17113           }
17114           out += this.renderer.link(href, null, text);
17115           continue;
17116         }
17117     
17118         // url (gfm)
17119         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17120           src = src.substring(cap[0].length);
17121           text = escape(cap[1]);
17122           href = text;
17123           out += this.renderer.link(href, null, text);
17124           continue;
17125         }
17126     
17127         // tag
17128         if (cap = this.rules.tag.exec(src)) {
17129           if (!this.inLink && /^<a /i.test(cap[0])) {
17130             this.inLink = true;
17131           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17132             this.inLink = false;
17133           }
17134           src = src.substring(cap[0].length);
17135           out += this.options.sanitize
17136             ? this.options.sanitizer
17137               ? this.options.sanitizer(cap[0])
17138               : escape(cap[0])
17139             : cap[0];
17140           continue;
17141         }
17142     
17143         // link
17144         if (cap = this.rules.link.exec(src)) {
17145           src = src.substring(cap[0].length);
17146           this.inLink = true;
17147           out += this.outputLink(cap, {
17148             href: cap[2],
17149             title: cap[3]
17150           });
17151           this.inLink = false;
17152           continue;
17153         }
17154     
17155         // reflink, nolink
17156         if ((cap = this.rules.reflink.exec(src))
17157             || (cap = this.rules.nolink.exec(src))) {
17158           src = src.substring(cap[0].length);
17159           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17160           link = this.links[link.toLowerCase()];
17161           if (!link || !link.href) {
17162             out += cap[0].charAt(0);
17163             src = cap[0].substring(1) + src;
17164             continue;
17165           }
17166           this.inLink = true;
17167           out += this.outputLink(cap, link);
17168           this.inLink = false;
17169           continue;
17170         }
17171     
17172         // strong
17173         if (cap = this.rules.strong.exec(src)) {
17174           src = src.substring(cap[0].length);
17175           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17176           continue;
17177         }
17178     
17179         // em
17180         if (cap = this.rules.em.exec(src)) {
17181           src = src.substring(cap[0].length);
17182           out += this.renderer.em(this.output(cap[2] || cap[1]));
17183           continue;
17184         }
17185     
17186         // code
17187         if (cap = this.rules.code.exec(src)) {
17188           src = src.substring(cap[0].length);
17189           out += this.renderer.codespan(escape(cap[2], true));
17190           continue;
17191         }
17192     
17193         // br
17194         if (cap = this.rules.br.exec(src)) {
17195           src = src.substring(cap[0].length);
17196           out += this.renderer.br();
17197           continue;
17198         }
17199     
17200         // del (gfm)
17201         if (cap = this.rules.del.exec(src)) {
17202           src = src.substring(cap[0].length);
17203           out += this.renderer.del(this.output(cap[1]));
17204           continue;
17205         }
17206     
17207         // text
17208         if (cap = this.rules.text.exec(src)) {
17209           src = src.substring(cap[0].length);
17210           out += this.renderer.text(escape(this.smartypants(cap[0])));
17211           continue;
17212         }
17213     
17214         if (src) {
17215           throw new
17216             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17217         }
17218       }
17219     
17220       return out;
17221     };
17222     
17223     /**
17224      * Compile Link
17225      */
17226     
17227     InlineLexer.prototype.outputLink = function(cap, link) {
17228       var href = escape(link.href)
17229         , title = link.title ? escape(link.title) : null;
17230     
17231       return cap[0].charAt(0) !== '!'
17232         ? this.renderer.link(href, title, this.output(cap[1]))
17233         : this.renderer.image(href, title, escape(cap[1]));
17234     };
17235     
17236     /**
17237      * Smartypants Transformations
17238      */
17239     
17240     InlineLexer.prototype.smartypants = function(text) {
17241       if (!this.options.smartypants)  { return text; }
17242       return text
17243         // em-dashes
17244         .replace(/---/g, '\u2014')
17245         // en-dashes
17246         .replace(/--/g, '\u2013')
17247         // opening singles
17248         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17249         // closing singles & apostrophes
17250         .replace(/'/g, '\u2019')
17251         // opening doubles
17252         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17253         // closing doubles
17254         .replace(/"/g, '\u201d')
17255         // ellipses
17256         .replace(/\.{3}/g, '\u2026');
17257     };
17258     
17259     /**
17260      * Mangle Links
17261      */
17262     
17263     InlineLexer.prototype.mangle = function(text) {
17264       if (!this.options.mangle) { return text; }
17265       var out = ''
17266         , l = text.length
17267         , i = 0
17268         , ch;
17269     
17270       for (; i < l; i++) {
17271         ch = text.charCodeAt(i);
17272         if (Math.random() > 0.5) {
17273           ch = 'x' + ch.toString(16);
17274         }
17275         out += '&#' + ch + ';';
17276       }
17277     
17278       return out;
17279     };
17280     
17281     /**
17282      * Renderer
17283      */
17284     
17285     function Renderer(options) {
17286       this.options = options || {};
17287     }
17288     
17289     Renderer.prototype.code = function(code, lang, escaped) {
17290       if (this.options.highlight) {
17291         var out = this.options.highlight(code, lang);
17292         if (out != null && out !== code) {
17293           escaped = true;
17294           code = out;
17295         }
17296       } else {
17297             // hack!!! - it's already escapeD?
17298             escaped = true;
17299       }
17300     
17301       if (!lang) {
17302         return '<pre><code>'
17303           + (escaped ? code : escape(code, true))
17304           + '\n</code></pre>';
17305       }
17306     
17307       return '<pre><code class="'
17308         + this.options.langPrefix
17309         + escape(lang, true)
17310         + '">'
17311         + (escaped ? code : escape(code, true))
17312         + '\n</code></pre>\n';
17313     };
17314     
17315     Renderer.prototype.blockquote = function(quote) {
17316       return '<blockquote>\n' + quote + '</blockquote>\n';
17317     };
17318     
17319     Renderer.prototype.html = function(html) {
17320       return html;
17321     };
17322     
17323     Renderer.prototype.heading = function(text, level, raw) {
17324       return '<h'
17325         + level
17326         + ' id="'
17327         + this.options.headerPrefix
17328         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17329         + '">'
17330         + text
17331         + '</h'
17332         + level
17333         + '>\n';
17334     };
17335     
17336     Renderer.prototype.hr = function() {
17337       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17338     };
17339     
17340     Renderer.prototype.list = function(body, ordered) {
17341       var type = ordered ? 'ol' : 'ul';
17342       return '<' + type + '>\n' + body + '</' + type + '>\n';
17343     };
17344     
17345     Renderer.prototype.listitem = function(text) {
17346       return '<li>' + text + '</li>\n';
17347     };
17348     
17349     Renderer.prototype.paragraph = function(text) {
17350       return '<p>' + text + '</p>\n';
17351     };
17352     
17353     Renderer.prototype.table = function(header, body) {
17354       return '<table>\n'
17355         + '<thead>\n'
17356         + header
17357         + '</thead>\n'
17358         + '<tbody>\n'
17359         + body
17360         + '</tbody>\n'
17361         + '</table>\n';
17362     };
17363     
17364     Renderer.prototype.tablerow = function(content) {
17365       return '<tr>\n' + content + '</tr>\n';
17366     };
17367     
17368     Renderer.prototype.tablecell = function(content, flags) {
17369       var type = flags.header ? 'th' : 'td';
17370       var tag = flags.align
17371         ? '<' + type + ' style="text-align:' + flags.align + '">'
17372         : '<' + type + '>';
17373       return tag + content + '</' + type + '>\n';
17374     };
17375     
17376     // span level renderer
17377     Renderer.prototype.strong = function(text) {
17378       return '<strong>' + text + '</strong>';
17379     };
17380     
17381     Renderer.prototype.em = function(text) {
17382       return '<em>' + text + '</em>';
17383     };
17384     
17385     Renderer.prototype.codespan = function(text) {
17386       return '<code>' + text + '</code>';
17387     };
17388     
17389     Renderer.prototype.br = function() {
17390       return this.options.xhtml ? '<br/>' : '<br>';
17391     };
17392     
17393     Renderer.prototype.del = function(text) {
17394       return '<del>' + text + '</del>';
17395     };
17396     
17397     Renderer.prototype.link = function(href, title, text) {
17398       if (this.options.sanitize) {
17399         try {
17400           var prot = decodeURIComponent(unescape(href))
17401             .replace(/[^\w:]/g, '')
17402             .toLowerCase();
17403         } catch (e) {
17404           return '';
17405         }
17406         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17407           return '';
17408         }
17409       }
17410       var out = '<a href="' + href + '"';
17411       if (title) {
17412         out += ' title="' + title + '"';
17413       }
17414       out += '>' + text + '</a>';
17415       return out;
17416     };
17417     
17418     Renderer.prototype.image = function(href, title, text) {
17419       var out = '<img src="' + href + '" alt="' + text + '"';
17420       if (title) {
17421         out += ' title="' + title + '"';
17422       }
17423       out += this.options.xhtml ? '/>' : '>';
17424       return out;
17425     };
17426     
17427     Renderer.prototype.text = function(text) {
17428       return text;
17429     };
17430     
17431     /**
17432      * Parsing & Compiling
17433      */
17434     
17435     function Parser(options) {
17436       this.tokens = [];
17437       this.token = null;
17438       this.options = options || marked.defaults;
17439       this.options.renderer = this.options.renderer || new Renderer;
17440       this.renderer = this.options.renderer;
17441       this.renderer.options = this.options;
17442     }
17443     
17444     /**
17445      * Static Parse Method
17446      */
17447     
17448     Parser.parse = function(src, options, renderer) {
17449       var parser = new Parser(options, renderer);
17450       return parser.parse(src);
17451     };
17452     
17453     /**
17454      * Parse Loop
17455      */
17456     
17457     Parser.prototype.parse = function(src) {
17458       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17459       this.tokens = src.reverse();
17460     
17461       var out = '';
17462       while (this.next()) {
17463         out += this.tok();
17464       }
17465     
17466       return out;
17467     };
17468     
17469     /**
17470      * Next Token
17471      */
17472     
17473     Parser.prototype.next = function() {
17474       return this.token = this.tokens.pop();
17475     };
17476     
17477     /**
17478      * Preview Next Token
17479      */
17480     
17481     Parser.prototype.peek = function() {
17482       return this.tokens[this.tokens.length - 1] || 0;
17483     };
17484     
17485     /**
17486      * Parse Text Tokens
17487      */
17488     
17489     Parser.prototype.parseText = function() {
17490       var body = this.token.text;
17491     
17492       while (this.peek().type === 'text') {
17493         body += '\n' + this.next().text;
17494       }
17495     
17496       return this.inline.output(body);
17497     };
17498     
17499     /**
17500      * Parse Current Token
17501      */
17502     
17503     Parser.prototype.tok = function() {
17504       switch (this.token.type) {
17505         case 'space': {
17506           return '';
17507         }
17508         case 'hr': {
17509           return this.renderer.hr();
17510         }
17511         case 'heading': {
17512           return this.renderer.heading(
17513             this.inline.output(this.token.text),
17514             this.token.depth,
17515             this.token.text);
17516         }
17517         case 'code': {
17518           return this.renderer.code(this.token.text,
17519             this.token.lang,
17520             this.token.escaped);
17521         }
17522         case 'table': {
17523           var header = ''
17524             , body = ''
17525             , i
17526             , row
17527             , cell
17528             , flags
17529             , j;
17530     
17531           // header
17532           cell = '';
17533           for (i = 0; i < this.token.header.length; i++) {
17534             flags = { header: true, align: this.token.align[i] };
17535             cell += this.renderer.tablecell(
17536               this.inline.output(this.token.header[i]),
17537               { header: true, align: this.token.align[i] }
17538             );
17539           }
17540           header += this.renderer.tablerow(cell);
17541     
17542           for (i = 0; i < this.token.cells.length; i++) {
17543             row = this.token.cells[i];
17544     
17545             cell = '';
17546             for (j = 0; j < row.length; j++) {
17547               cell += this.renderer.tablecell(
17548                 this.inline.output(row[j]),
17549                 { header: false, align: this.token.align[j] }
17550               );
17551             }
17552     
17553             body += this.renderer.tablerow(cell);
17554           }
17555           return this.renderer.table(header, body);
17556         }
17557         case 'blockquote_start': {
17558           var body = '';
17559     
17560           while (this.next().type !== 'blockquote_end') {
17561             body += this.tok();
17562           }
17563     
17564           return this.renderer.blockquote(body);
17565         }
17566         case 'list_start': {
17567           var body = ''
17568             , ordered = this.token.ordered;
17569     
17570           while (this.next().type !== 'list_end') {
17571             body += this.tok();
17572           }
17573     
17574           return this.renderer.list(body, ordered);
17575         }
17576         case 'list_item_start': {
17577           var body = '';
17578     
17579           while (this.next().type !== 'list_item_end') {
17580             body += this.token.type === 'text'
17581               ? this.parseText()
17582               : this.tok();
17583           }
17584     
17585           return this.renderer.listitem(body);
17586         }
17587         case 'loose_item_start': {
17588           var body = '';
17589     
17590           while (this.next().type !== 'list_item_end') {
17591             body += this.tok();
17592           }
17593     
17594           return this.renderer.listitem(body);
17595         }
17596         case 'html': {
17597           var html = !this.token.pre && !this.options.pedantic
17598             ? this.inline.output(this.token.text)
17599             : this.token.text;
17600           return this.renderer.html(html);
17601         }
17602         case 'paragraph': {
17603           return this.renderer.paragraph(this.inline.output(this.token.text));
17604         }
17605         case 'text': {
17606           return this.renderer.paragraph(this.parseText());
17607         }
17608       }
17609     };
17610     
17611     /**
17612      * Helpers
17613      */
17614     
17615     function escape(html, encode) {
17616       return html
17617         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17618         .replace(/</g, '&lt;')
17619         .replace(/>/g, '&gt;')
17620         .replace(/"/g, '&quot;')
17621         .replace(/'/g, '&#39;');
17622     }
17623     
17624     function unescape(html) {
17625         // explicitly match decimal, hex, and named HTML entities 
17626       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17627         n = n.toLowerCase();
17628         if (n === 'colon') { return ':'; }
17629         if (n.charAt(0) === '#') {
17630           return n.charAt(1) === 'x'
17631             ? String.fromCharCode(parseInt(n.substring(2), 16))
17632             : String.fromCharCode(+n.substring(1));
17633         }
17634         return '';
17635       });
17636     }
17637     
17638     function replace(regex, opt) {
17639       regex = regex.source;
17640       opt = opt || '';
17641       return function self(name, val) {
17642         if (!name) { return new RegExp(regex, opt); }
17643         val = val.source || val;
17644         val = val.replace(/(^|[^\[])\^/g, '$1');
17645         regex = regex.replace(name, val);
17646         return self;
17647       };
17648     }
17649     
17650     function noop() {}
17651     noop.exec = noop;
17652     
17653     function merge(obj) {
17654       var i = 1
17655         , target
17656         , key;
17657     
17658       for (; i < arguments.length; i++) {
17659         target = arguments[i];
17660         for (key in target) {
17661           if (Object.prototype.hasOwnProperty.call(target, key)) {
17662             obj[key] = target[key];
17663           }
17664         }
17665       }
17666     
17667       return obj;
17668     }
17669     
17670     
17671     /**
17672      * Marked
17673      */
17674     
17675     function marked(src, opt, callback) {
17676       if (callback || typeof opt === 'function') {
17677         if (!callback) {
17678           callback = opt;
17679           opt = null;
17680         }
17681     
17682         opt = merge({}, marked.defaults, opt || {});
17683     
17684         var highlight = opt.highlight
17685           , tokens
17686           , pending
17687           , i = 0;
17688     
17689         try {
17690           tokens = Lexer.lex(src, opt)
17691         } catch (e) {
17692           return callback(e);
17693         }
17694     
17695         pending = tokens.length;
17696     
17697         var done = function(err) {
17698           if (err) {
17699             opt.highlight = highlight;
17700             return callback(err);
17701           }
17702     
17703           var out;
17704     
17705           try {
17706             out = Parser.parse(tokens, opt);
17707           } catch (e) {
17708             err = e;
17709           }
17710     
17711           opt.highlight = highlight;
17712     
17713           return err
17714             ? callback(err)
17715             : callback(null, out);
17716         };
17717     
17718         if (!highlight || highlight.length < 3) {
17719           return done();
17720         }
17721     
17722         delete opt.highlight;
17723     
17724         if (!pending) { return done(); }
17725     
17726         for (; i < tokens.length; i++) {
17727           (function(token) {
17728             if (token.type !== 'code') {
17729               return --pending || done();
17730             }
17731             return highlight(token.text, token.lang, function(err, code) {
17732               if (err) { return done(err); }
17733               if (code == null || code === token.text) {
17734                 return --pending || done();
17735               }
17736               token.text = code;
17737               token.escaped = true;
17738               --pending || done();
17739             });
17740           })(tokens[i]);
17741         }
17742     
17743         return;
17744       }
17745       try {
17746         if (opt) { opt = merge({}, marked.defaults, opt); }
17747         return Parser.parse(Lexer.lex(src, opt), opt);
17748       } catch (e) {
17749         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17750         if ((opt || marked.defaults).silent) {
17751           return '<p>An error occured:</p><pre>'
17752             + escape(e.message + '', true)
17753             + '</pre>';
17754         }
17755         throw e;
17756       }
17757     }
17758     
17759     /**
17760      * Options
17761      */
17762     
17763     marked.options =
17764     marked.setOptions = function(opt) {
17765       merge(marked.defaults, opt);
17766       return marked;
17767     };
17768     
17769     marked.defaults = {
17770       gfm: true,
17771       tables: true,
17772       breaks: false,
17773       pedantic: false,
17774       sanitize: false,
17775       sanitizer: null,
17776       mangle: true,
17777       smartLists: false,
17778       silent: false,
17779       highlight: null,
17780       langPrefix: 'lang-',
17781       smartypants: false,
17782       headerPrefix: '',
17783       renderer: new Renderer,
17784       xhtml: false
17785     };
17786     
17787     /**
17788      * Expose
17789      */
17790     
17791     marked.Parser = Parser;
17792     marked.parser = Parser.parse;
17793     
17794     marked.Renderer = Renderer;
17795     
17796     marked.Lexer = Lexer;
17797     marked.lexer = Lexer.lex;
17798     
17799     marked.InlineLexer = InlineLexer;
17800     marked.inlineLexer = InlineLexer.output;
17801     
17802     marked.parse = marked;
17803     
17804     Roo.Markdown.marked = marked;
17805
17806 })();/*
17807  * Based on:
17808  * Ext JS Library 1.1.1
17809  * Copyright(c) 2006-2007, Ext JS, LLC.
17810  *
17811  * Originally Released Under LGPL - original licence link has changed is not relivant.
17812  *
17813  * Fork - LGPL
17814  * <script type="text/javascript">
17815  */
17816
17817
17818
17819 /*
17820  * These classes are derivatives of the similarly named classes in the YUI Library.
17821  * The original license:
17822  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17823  * Code licensed under the BSD License:
17824  * http://developer.yahoo.net/yui/license.txt
17825  */
17826
17827 (function() {
17828
17829 var Event=Roo.EventManager;
17830 var Dom=Roo.lib.Dom;
17831
17832 /**
17833  * @class Roo.dd.DragDrop
17834  * @extends Roo.util.Observable
17835  * Defines the interface and base operation of items that that can be
17836  * dragged or can be drop targets.  It was designed to be extended, overriding
17837  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17838  * Up to three html elements can be associated with a DragDrop instance:
17839  * <ul>
17840  * <li>linked element: the element that is passed into the constructor.
17841  * This is the element which defines the boundaries for interaction with
17842  * other DragDrop objects.</li>
17843  * <li>handle element(s): The drag operation only occurs if the element that
17844  * was clicked matches a handle element.  By default this is the linked
17845  * element, but there are times that you will want only a portion of the
17846  * linked element to initiate the drag operation, and the setHandleElId()
17847  * method provides a way to define this.</li>
17848  * <li>drag element: this represents the element that would be moved along
17849  * with the cursor during a drag operation.  By default, this is the linked
17850  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17851  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17852  * </li>
17853  * </ul>
17854  * This class should not be instantiated until the onload event to ensure that
17855  * the associated elements are available.
17856  * The following would define a DragDrop obj that would interact with any
17857  * other DragDrop obj in the "group1" group:
17858  * <pre>
17859  *  dd = new Roo.dd.DragDrop("div1", "group1");
17860  * </pre>
17861  * Since none of the event handlers have been implemented, nothing would
17862  * actually happen if you were to run the code above.  Normally you would
17863  * override this class or one of the default implementations, but you can
17864  * also override the methods you want on an instance of the class...
17865  * <pre>
17866  *  dd.onDragDrop = function(e, id) {
17867  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
17868  *  }
17869  * </pre>
17870  * @constructor
17871  * @param {String} id of the element that is linked to this instance
17872  * @param {String} sGroup the group of related DragDrop objects
17873  * @param {object} config an object containing configurable attributes
17874  *                Valid properties for DragDrop:
17875  *                    padding, isTarget, maintainOffset, primaryButtonOnly
17876  */
17877 Roo.dd.DragDrop = function(id, sGroup, config) {
17878     if (id) {
17879         this.init(id, sGroup, config);
17880     }
17881     
17882 };
17883
17884 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
17885
17886     /**
17887      * The id of the element associated with this object.  This is what we
17888      * refer to as the "linked element" because the size and position of
17889      * this element is used to determine when the drag and drop objects have
17890      * interacted.
17891      * @property id
17892      * @type String
17893      */
17894     id: null,
17895
17896     /**
17897      * Configuration attributes passed into the constructor
17898      * @property config
17899      * @type object
17900      */
17901     config: null,
17902
17903     /**
17904      * The id of the element that will be dragged.  By default this is same
17905      * as the linked element , but could be changed to another element. Ex:
17906      * Roo.dd.DDProxy
17907      * @property dragElId
17908      * @type String
17909      * @private
17910      */
17911     dragElId: null,
17912
17913     /**
17914      * the id of the element that initiates the drag operation.  By default
17915      * this is the linked element, but could be changed to be a child of this
17916      * element.  This lets us do things like only starting the drag when the
17917      * header element within the linked html element is clicked.
17918      * @property handleElId
17919      * @type String
17920      * @private
17921      */
17922     handleElId: null,
17923
17924     /**
17925      * An associative array of HTML tags that will be ignored if clicked.
17926      * @property invalidHandleTypes
17927      * @type {string: string}
17928      */
17929     invalidHandleTypes: null,
17930
17931     /**
17932      * An associative array of ids for elements that will be ignored if clicked
17933      * @property invalidHandleIds
17934      * @type {string: string}
17935      */
17936     invalidHandleIds: null,
17937
17938     /**
17939      * An indexted array of css class names for elements that will be ignored
17940      * if clicked.
17941      * @property invalidHandleClasses
17942      * @type string[]
17943      */
17944     invalidHandleClasses: null,
17945
17946     /**
17947      * The linked element's absolute X position at the time the drag was
17948      * started
17949      * @property startPageX
17950      * @type int
17951      * @private
17952      */
17953     startPageX: 0,
17954
17955     /**
17956      * The linked element's absolute X position at the time the drag was
17957      * started
17958      * @property startPageY
17959      * @type int
17960      * @private
17961      */
17962     startPageY: 0,
17963
17964     /**
17965      * The group defines a logical collection of DragDrop objects that are
17966      * related.  Instances only get events when interacting with other
17967      * DragDrop object in the same group.  This lets us define multiple
17968      * groups using a single DragDrop subclass if we want.
17969      * @property groups
17970      * @type {string: string}
17971      */
17972     groups: null,
17973
17974     /**
17975      * Individual drag/drop instances can be locked.  This will prevent
17976      * onmousedown start drag.
17977      * @property locked
17978      * @type boolean
17979      * @private
17980      */
17981     locked: false,
17982
17983     /**
17984      * Lock this instance
17985      * @method lock
17986      */
17987     lock: function() { this.locked = true; },
17988
17989     /**
17990      * Unlock this instace
17991      * @method unlock
17992      */
17993     unlock: function() { this.locked = false; },
17994
17995     /**
17996      * By default, all insances can be a drop target.  This can be disabled by
17997      * setting isTarget to false.
17998      * @method isTarget
17999      * @type boolean
18000      */
18001     isTarget: true,
18002
18003     /**
18004      * The padding configured for this drag and drop object for calculating
18005      * the drop zone intersection with this object.
18006      * @method padding
18007      * @type int[]
18008      */
18009     padding: null,
18010
18011     /**
18012      * Cached reference to the linked element
18013      * @property _domRef
18014      * @private
18015      */
18016     _domRef: null,
18017
18018     /**
18019      * Internal typeof flag
18020      * @property __ygDragDrop
18021      * @private
18022      */
18023     __ygDragDrop: true,
18024
18025     /**
18026      * Set to true when horizontal contraints are applied
18027      * @property constrainX
18028      * @type boolean
18029      * @private
18030      */
18031     constrainX: false,
18032
18033     /**
18034      * Set to true when vertical contraints are applied
18035      * @property constrainY
18036      * @type boolean
18037      * @private
18038      */
18039     constrainY: false,
18040
18041     /**
18042      * The left constraint
18043      * @property minX
18044      * @type int
18045      * @private
18046      */
18047     minX: 0,
18048
18049     /**
18050      * The right constraint
18051      * @property maxX
18052      * @type int
18053      * @private
18054      */
18055     maxX: 0,
18056
18057     /**
18058      * The up constraint
18059      * @property minY
18060      * @type int
18061      * @type int
18062      * @private
18063      */
18064     minY: 0,
18065
18066     /**
18067      * The down constraint
18068      * @property maxY
18069      * @type int
18070      * @private
18071      */
18072     maxY: 0,
18073
18074     /**
18075      * Maintain offsets when we resetconstraints.  Set to true when you want
18076      * the position of the element relative to its parent to stay the same
18077      * when the page changes
18078      *
18079      * @property maintainOffset
18080      * @type boolean
18081      */
18082     maintainOffset: false,
18083
18084     /**
18085      * Array of pixel locations the element will snap to if we specified a
18086      * horizontal graduation/interval.  This array is generated automatically
18087      * when you define a tick interval.
18088      * @property xTicks
18089      * @type int[]
18090      */
18091     xTicks: null,
18092
18093     /**
18094      * Array of pixel locations the element will snap to if we specified a
18095      * vertical graduation/interval.  This array is generated automatically
18096      * when you define a tick interval.
18097      * @property yTicks
18098      * @type int[]
18099      */
18100     yTicks: null,
18101
18102     /**
18103      * By default the drag and drop instance will only respond to the primary
18104      * button click (left button for a right-handed mouse).  Set to true to
18105      * allow drag and drop to start with any mouse click that is propogated
18106      * by the browser
18107      * @property primaryButtonOnly
18108      * @type boolean
18109      */
18110     primaryButtonOnly: true,
18111
18112     /**
18113      * The availabe property is false until the linked dom element is accessible.
18114      * @property available
18115      * @type boolean
18116      */
18117     available: false,
18118
18119     /**
18120      * By default, drags can only be initiated if the mousedown occurs in the
18121      * region the linked element is.  This is done in part to work around a
18122      * bug in some browsers that mis-report the mousedown if the previous
18123      * mouseup happened outside of the window.  This property is set to true
18124      * if outer handles are defined.
18125      *
18126      * @property hasOuterHandles
18127      * @type boolean
18128      * @default false
18129      */
18130     hasOuterHandles: false,
18131
18132     /**
18133      * Code that executes immediately before the startDrag event
18134      * @method b4StartDrag
18135      * @private
18136      */
18137     b4StartDrag: function(x, y) { },
18138
18139     /**
18140      * Abstract method called after a drag/drop object is clicked
18141      * and the drag or mousedown time thresholds have beeen met.
18142      * @method startDrag
18143      * @param {int} X click location
18144      * @param {int} Y click location
18145      */
18146     startDrag: function(x, y) { /* override this */ },
18147
18148     /**
18149      * Code that executes immediately before the onDrag event
18150      * @method b4Drag
18151      * @private
18152      */
18153     b4Drag: function(e) { },
18154
18155     /**
18156      * Abstract method called during the onMouseMove event while dragging an
18157      * object.
18158      * @method onDrag
18159      * @param {Event} e the mousemove event
18160      */
18161     onDrag: function(e) { /* override this */ },
18162
18163     /**
18164      * Abstract method called when this element fist begins hovering over
18165      * another DragDrop obj
18166      * @method onDragEnter
18167      * @param {Event} e the mousemove event
18168      * @param {String|DragDrop[]} id In POINT mode, the element
18169      * id this is hovering over.  In INTERSECT mode, an array of one or more
18170      * dragdrop items being hovered over.
18171      */
18172     onDragEnter: function(e, id) { /* override this */ },
18173
18174     /**
18175      * Code that executes immediately before the onDragOver event
18176      * @method b4DragOver
18177      * @private
18178      */
18179     b4DragOver: function(e) { },
18180
18181     /**
18182      * Abstract method called when this element is hovering over another
18183      * DragDrop obj
18184      * @method onDragOver
18185      * @param {Event} e the mousemove event
18186      * @param {String|DragDrop[]} id In POINT mode, the element
18187      * id this is hovering over.  In INTERSECT mode, an array of dd items
18188      * being hovered over.
18189      */
18190     onDragOver: function(e, id) { /* override this */ },
18191
18192     /**
18193      * Code that executes immediately before the onDragOut event
18194      * @method b4DragOut
18195      * @private
18196      */
18197     b4DragOut: function(e) { },
18198
18199     /**
18200      * Abstract method called when we are no longer hovering over an element
18201      * @method onDragOut
18202      * @param {Event} e the mousemove event
18203      * @param {String|DragDrop[]} id In POINT mode, the element
18204      * id this was hovering over.  In INTERSECT mode, an array of dd items
18205      * that the mouse is no longer over.
18206      */
18207     onDragOut: function(e, id) { /* override this */ },
18208
18209     /**
18210      * Code that executes immediately before the onDragDrop event
18211      * @method b4DragDrop
18212      * @private
18213      */
18214     b4DragDrop: function(e) { },
18215
18216     /**
18217      * Abstract method called when this item is dropped on another DragDrop
18218      * obj
18219      * @method onDragDrop
18220      * @param {Event} e the mouseup event
18221      * @param {String|DragDrop[]} id In POINT mode, the element
18222      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18223      * was dropped on.
18224      */
18225     onDragDrop: function(e, id) { /* override this */ },
18226
18227     /**
18228      * Abstract method called when this item is dropped on an area with no
18229      * drop target
18230      * @method onInvalidDrop
18231      * @param {Event} e the mouseup event
18232      */
18233     onInvalidDrop: function(e) { /* override this */ },
18234
18235     /**
18236      * Code that executes immediately before the endDrag event
18237      * @method b4EndDrag
18238      * @private
18239      */
18240     b4EndDrag: function(e) { },
18241
18242     /**
18243      * Fired when we are done dragging the object
18244      * @method endDrag
18245      * @param {Event} e the mouseup event
18246      */
18247     endDrag: function(e) { /* override this */ },
18248
18249     /**
18250      * Code executed immediately before the onMouseDown event
18251      * @method b4MouseDown
18252      * @param {Event} e the mousedown event
18253      * @private
18254      */
18255     b4MouseDown: function(e) {  },
18256
18257     /**
18258      * Event handler that fires when a drag/drop obj gets a mousedown
18259      * @method onMouseDown
18260      * @param {Event} e the mousedown event
18261      */
18262     onMouseDown: function(e) { /* override this */ },
18263
18264     /**
18265      * Event handler that fires when a drag/drop obj gets a mouseup
18266      * @method onMouseUp
18267      * @param {Event} e the mouseup event
18268      */
18269     onMouseUp: function(e) { /* override this */ },
18270
18271     /**
18272      * Override the onAvailable method to do what is needed after the initial
18273      * position was determined.
18274      * @method onAvailable
18275      */
18276     onAvailable: function () {
18277     },
18278
18279     /*
18280      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18281      * @type Object
18282      */
18283     defaultPadding : {left:0, right:0, top:0, bottom:0},
18284
18285     /*
18286      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18287  *
18288  * Usage:
18289  <pre><code>
18290  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18291                 { dragElId: "existingProxyDiv" });
18292  dd.startDrag = function(){
18293      this.constrainTo("parent-id");
18294  };
18295  </code></pre>
18296  * Or you can initalize it using the {@link Roo.Element} object:
18297  <pre><code>
18298  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18299      startDrag : function(){
18300          this.constrainTo("parent-id");
18301      }
18302  });
18303  </code></pre>
18304      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18305      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18306      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18307      * an object containing the sides to pad. For example: {right:10, bottom:10}
18308      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18309      */
18310     constrainTo : function(constrainTo, pad, inContent){
18311         if(typeof pad == "number"){
18312             pad = {left: pad, right:pad, top:pad, bottom:pad};
18313         }
18314         pad = pad || this.defaultPadding;
18315         var b = Roo.get(this.getEl()).getBox();
18316         var ce = Roo.get(constrainTo);
18317         var s = ce.getScroll();
18318         var c, cd = ce.dom;
18319         if(cd == document.body){
18320             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18321         }else{
18322             xy = ce.getXY();
18323             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18324         }
18325
18326
18327         var topSpace = b.y - c.y;
18328         var leftSpace = b.x - c.x;
18329
18330         this.resetConstraints();
18331         this.setXConstraint(leftSpace - (pad.left||0), // left
18332                 c.width - leftSpace - b.width - (pad.right||0) //right
18333         );
18334         this.setYConstraint(topSpace - (pad.top||0), //top
18335                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18336         );
18337     },
18338
18339     /**
18340      * Returns a reference to the linked element
18341      * @method getEl
18342      * @return {HTMLElement} the html element
18343      */
18344     getEl: function() {
18345         if (!this._domRef) {
18346             this._domRef = Roo.getDom(this.id);
18347         }
18348
18349         return this._domRef;
18350     },
18351
18352     /**
18353      * Returns a reference to the actual element to drag.  By default this is
18354      * the same as the html element, but it can be assigned to another
18355      * element. An example of this can be found in Roo.dd.DDProxy
18356      * @method getDragEl
18357      * @return {HTMLElement} the html element
18358      */
18359     getDragEl: function() {
18360         return Roo.getDom(this.dragElId);
18361     },
18362
18363     /**
18364      * Sets up the DragDrop object.  Must be called in the constructor of any
18365      * Roo.dd.DragDrop subclass
18366      * @method init
18367      * @param id the id of the linked element
18368      * @param {String} sGroup the group of related items
18369      * @param {object} config configuration attributes
18370      */
18371     init: function(id, sGroup, config) {
18372         this.initTarget(id, sGroup, config);
18373         if (!Roo.isTouch) {
18374             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18375         }
18376         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18377         // Event.on(this.id, "selectstart", Event.preventDefault);
18378     },
18379
18380     /**
18381      * Initializes Targeting functionality only... the object does not
18382      * get a mousedown handler.
18383      * @method initTarget
18384      * @param id the id of the linked element
18385      * @param {String} sGroup the group of related items
18386      * @param {object} config configuration attributes
18387      */
18388     initTarget: function(id, sGroup, config) {
18389
18390         // configuration attributes
18391         this.config = config || {};
18392
18393         // create a local reference to the drag and drop manager
18394         this.DDM = Roo.dd.DDM;
18395         // initialize the groups array
18396         this.groups = {};
18397
18398         // assume that we have an element reference instead of an id if the
18399         // parameter is not a string
18400         if (typeof id !== "string") {
18401             id = Roo.id(id);
18402         }
18403
18404         // set the id
18405         this.id = id;
18406
18407         // add to an interaction group
18408         this.addToGroup((sGroup) ? sGroup : "default");
18409
18410         // We don't want to register this as the handle with the manager
18411         // so we just set the id rather than calling the setter.
18412         this.handleElId = id;
18413
18414         // the linked element is the element that gets dragged by default
18415         this.setDragElId(id);
18416
18417         // by default, clicked anchors will not start drag operations.
18418         this.invalidHandleTypes = { A: "A" };
18419         this.invalidHandleIds = {};
18420         this.invalidHandleClasses = [];
18421
18422         this.applyConfig();
18423
18424         this.handleOnAvailable();
18425     },
18426
18427     /**
18428      * Applies the configuration parameters that were passed into the constructor.
18429      * This is supposed to happen at each level through the inheritance chain.  So
18430      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18431      * DragDrop in order to get all of the parameters that are available in
18432      * each object.
18433      * @method applyConfig
18434      */
18435     applyConfig: function() {
18436
18437         // configurable properties:
18438         //    padding, isTarget, maintainOffset, primaryButtonOnly
18439         this.padding           = this.config.padding || [0, 0, 0, 0];
18440         this.isTarget          = (this.config.isTarget !== false);
18441         this.maintainOffset    = (this.config.maintainOffset);
18442         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18443
18444     },
18445
18446     /**
18447      * Executed when the linked element is available
18448      * @method handleOnAvailable
18449      * @private
18450      */
18451     handleOnAvailable: function() {
18452         this.available = true;
18453         this.resetConstraints();
18454         this.onAvailable();
18455     },
18456
18457      /**
18458      * Configures the padding for the target zone in px.  Effectively expands
18459      * (or reduces) the virtual object size for targeting calculations.
18460      * Supports css-style shorthand; if only one parameter is passed, all sides
18461      * will have that padding, and if only two are passed, the top and bottom
18462      * will have the first param, the left and right the second.
18463      * @method setPadding
18464      * @param {int} iTop    Top pad
18465      * @param {int} iRight  Right pad
18466      * @param {int} iBot    Bot pad
18467      * @param {int} iLeft   Left pad
18468      */
18469     setPadding: function(iTop, iRight, iBot, iLeft) {
18470         // this.padding = [iLeft, iRight, iTop, iBot];
18471         if (!iRight && 0 !== iRight) {
18472             this.padding = [iTop, iTop, iTop, iTop];
18473         } else if (!iBot && 0 !== iBot) {
18474             this.padding = [iTop, iRight, iTop, iRight];
18475         } else {
18476             this.padding = [iTop, iRight, iBot, iLeft];
18477         }
18478     },
18479
18480     /**
18481      * Stores the initial placement of the linked element.
18482      * @method setInitialPosition
18483      * @param {int} diffX   the X offset, default 0
18484      * @param {int} diffY   the Y offset, default 0
18485      */
18486     setInitPosition: function(diffX, diffY) {
18487         var el = this.getEl();
18488
18489         if (!this.DDM.verifyEl(el)) {
18490             return;
18491         }
18492
18493         var dx = diffX || 0;
18494         var dy = diffY || 0;
18495
18496         var p = Dom.getXY( el );
18497
18498         this.initPageX = p[0] - dx;
18499         this.initPageY = p[1] - dy;
18500
18501         this.lastPageX = p[0];
18502         this.lastPageY = p[1];
18503
18504
18505         this.setStartPosition(p);
18506     },
18507
18508     /**
18509      * Sets the start position of the element.  This is set when the obj
18510      * is initialized, the reset when a drag is started.
18511      * @method setStartPosition
18512      * @param pos current position (from previous lookup)
18513      * @private
18514      */
18515     setStartPosition: function(pos) {
18516         var p = pos || Dom.getXY( this.getEl() );
18517         this.deltaSetXY = null;
18518
18519         this.startPageX = p[0];
18520         this.startPageY = p[1];
18521     },
18522
18523     /**
18524      * Add this instance to a group of related drag/drop objects.  All
18525      * instances belong to at least one group, and can belong to as many
18526      * groups as needed.
18527      * @method addToGroup
18528      * @param sGroup {string} the name of the group
18529      */
18530     addToGroup: function(sGroup) {
18531         this.groups[sGroup] = true;
18532         this.DDM.regDragDrop(this, sGroup);
18533     },
18534
18535     /**
18536      * Remove's this instance from the supplied interaction group
18537      * @method removeFromGroup
18538      * @param {string}  sGroup  The group to drop
18539      */
18540     removeFromGroup: function(sGroup) {
18541         if (this.groups[sGroup]) {
18542             delete this.groups[sGroup];
18543         }
18544
18545         this.DDM.removeDDFromGroup(this, sGroup);
18546     },
18547
18548     /**
18549      * Allows you to specify that an element other than the linked element
18550      * will be moved with the cursor during a drag
18551      * @method setDragElId
18552      * @param id {string} the id of the element that will be used to initiate the drag
18553      */
18554     setDragElId: function(id) {
18555         this.dragElId = id;
18556     },
18557
18558     /**
18559      * Allows you to specify a child of the linked element that should be
18560      * used to initiate the drag operation.  An example of this would be if
18561      * you have a content div with text and links.  Clicking anywhere in the
18562      * content area would normally start the drag operation.  Use this method
18563      * to specify that an element inside of the content div is the element
18564      * that starts the drag operation.
18565      * @method setHandleElId
18566      * @param id {string} the id of the element that will be used to
18567      * initiate the drag.
18568      */
18569     setHandleElId: function(id) {
18570         if (typeof id !== "string") {
18571             id = Roo.id(id);
18572         }
18573         this.handleElId = id;
18574         this.DDM.regHandle(this.id, id);
18575     },
18576
18577     /**
18578      * Allows you to set an element outside of the linked element as a drag
18579      * handle
18580      * @method setOuterHandleElId
18581      * @param id the id of the element that will be used to initiate the drag
18582      */
18583     setOuterHandleElId: function(id) {
18584         if (typeof id !== "string") {
18585             id = Roo.id(id);
18586         }
18587         Event.on(id, "mousedown",
18588                 this.handleMouseDown, this);
18589         this.setHandleElId(id);
18590
18591         this.hasOuterHandles = true;
18592     },
18593
18594     /**
18595      * Remove all drag and drop hooks for this element
18596      * @method unreg
18597      */
18598     unreg: function() {
18599         Event.un(this.id, "mousedown",
18600                 this.handleMouseDown);
18601         Event.un(this.id, "touchstart",
18602                 this.handleMouseDown);
18603         this._domRef = null;
18604         this.DDM._remove(this);
18605     },
18606
18607     destroy : function(){
18608         this.unreg();
18609     },
18610
18611     /**
18612      * Returns true if this instance is locked, or the drag drop mgr is locked
18613      * (meaning that all drag/drop is disabled on the page.)
18614      * @method isLocked
18615      * @return {boolean} true if this obj or all drag/drop is locked, else
18616      * false
18617      */
18618     isLocked: function() {
18619         return (this.DDM.isLocked() || this.locked);
18620     },
18621
18622     /**
18623      * Fired when this object is clicked
18624      * @method handleMouseDown
18625      * @param {Event} e
18626      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18627      * @private
18628      */
18629     handleMouseDown: function(e, oDD){
18630      
18631         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18632             //Roo.log('not touch/ button !=0');
18633             return;
18634         }
18635         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18636             return; // double touch..
18637         }
18638         
18639
18640         if (this.isLocked()) {
18641             //Roo.log('locked');
18642             return;
18643         }
18644
18645         this.DDM.refreshCache(this.groups);
18646 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18647         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18648         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18649             //Roo.log('no outer handes or not over target');
18650                 // do nothing.
18651         } else {
18652 //            Roo.log('check validator');
18653             if (this.clickValidator(e)) {
18654 //                Roo.log('validate success');
18655                 // set the initial element position
18656                 this.setStartPosition();
18657
18658
18659                 this.b4MouseDown(e);
18660                 this.onMouseDown(e);
18661
18662                 this.DDM.handleMouseDown(e, this);
18663
18664                 this.DDM.stopEvent(e);
18665             } else {
18666
18667
18668             }
18669         }
18670     },
18671
18672     clickValidator: function(e) {
18673         var target = e.getTarget();
18674         return ( this.isValidHandleChild(target) &&
18675                     (this.id == this.handleElId ||
18676                         this.DDM.handleWasClicked(target, this.id)) );
18677     },
18678
18679     /**
18680      * Allows you to specify a tag name that should not start a drag operation
18681      * when clicked.  This is designed to facilitate embedding links within a
18682      * drag handle that do something other than start the drag.
18683      * @method addInvalidHandleType
18684      * @param {string} tagName the type of element to exclude
18685      */
18686     addInvalidHandleType: function(tagName) {
18687         var type = tagName.toUpperCase();
18688         this.invalidHandleTypes[type] = type;
18689     },
18690
18691     /**
18692      * Lets you to specify an element id for a child of a drag handle
18693      * that should not initiate a drag
18694      * @method addInvalidHandleId
18695      * @param {string} id the element id of the element you wish to ignore
18696      */
18697     addInvalidHandleId: function(id) {
18698         if (typeof id !== "string") {
18699             id = Roo.id(id);
18700         }
18701         this.invalidHandleIds[id] = id;
18702     },
18703
18704     /**
18705      * Lets you specify a css class of elements that will not initiate a drag
18706      * @method addInvalidHandleClass
18707      * @param {string} cssClass the class of the elements you wish to ignore
18708      */
18709     addInvalidHandleClass: function(cssClass) {
18710         this.invalidHandleClasses.push(cssClass);
18711     },
18712
18713     /**
18714      * Unsets an excluded tag name set by addInvalidHandleType
18715      * @method removeInvalidHandleType
18716      * @param {string} tagName the type of element to unexclude
18717      */
18718     removeInvalidHandleType: function(tagName) {
18719         var type = tagName.toUpperCase();
18720         // this.invalidHandleTypes[type] = null;
18721         delete this.invalidHandleTypes[type];
18722     },
18723
18724     /**
18725      * Unsets an invalid handle id
18726      * @method removeInvalidHandleId
18727      * @param {string} id the id of the element to re-enable
18728      */
18729     removeInvalidHandleId: function(id) {
18730         if (typeof id !== "string") {
18731             id = Roo.id(id);
18732         }
18733         delete this.invalidHandleIds[id];
18734     },
18735
18736     /**
18737      * Unsets an invalid css class
18738      * @method removeInvalidHandleClass
18739      * @param {string} cssClass the class of the element(s) you wish to
18740      * re-enable
18741      */
18742     removeInvalidHandleClass: function(cssClass) {
18743         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18744             if (this.invalidHandleClasses[i] == cssClass) {
18745                 delete this.invalidHandleClasses[i];
18746             }
18747         }
18748     },
18749
18750     /**
18751      * Checks the tag exclusion list to see if this click should be ignored
18752      * @method isValidHandleChild
18753      * @param {HTMLElement} node the HTMLElement to evaluate
18754      * @return {boolean} true if this is a valid tag type, false if not
18755      */
18756     isValidHandleChild: function(node) {
18757
18758         var valid = true;
18759         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18760         var nodeName;
18761         try {
18762             nodeName = node.nodeName.toUpperCase();
18763         } catch(e) {
18764             nodeName = node.nodeName;
18765         }
18766         valid = valid && !this.invalidHandleTypes[nodeName];
18767         valid = valid && !this.invalidHandleIds[node.id];
18768
18769         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18770             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18771         }
18772
18773
18774         return valid;
18775
18776     },
18777
18778     /**
18779      * Create the array of horizontal tick marks if an interval was specified
18780      * in setXConstraint().
18781      * @method setXTicks
18782      * @private
18783      */
18784     setXTicks: function(iStartX, iTickSize) {
18785         this.xTicks = [];
18786         this.xTickSize = iTickSize;
18787
18788         var tickMap = {};
18789
18790         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18791             if (!tickMap[i]) {
18792                 this.xTicks[this.xTicks.length] = i;
18793                 tickMap[i] = true;
18794             }
18795         }
18796
18797         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18798             if (!tickMap[i]) {
18799                 this.xTicks[this.xTicks.length] = i;
18800                 tickMap[i] = true;
18801             }
18802         }
18803
18804         this.xTicks.sort(this.DDM.numericSort) ;
18805     },
18806
18807     /**
18808      * Create the array of vertical tick marks if an interval was specified in
18809      * setYConstraint().
18810      * @method setYTicks
18811      * @private
18812      */
18813     setYTicks: function(iStartY, iTickSize) {
18814         this.yTicks = [];
18815         this.yTickSize = iTickSize;
18816
18817         var tickMap = {};
18818
18819         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18820             if (!tickMap[i]) {
18821                 this.yTicks[this.yTicks.length] = i;
18822                 tickMap[i] = true;
18823             }
18824         }
18825
18826         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18827             if (!tickMap[i]) {
18828                 this.yTicks[this.yTicks.length] = i;
18829                 tickMap[i] = true;
18830             }
18831         }
18832
18833         this.yTicks.sort(this.DDM.numericSort) ;
18834     },
18835
18836     /**
18837      * By default, the element can be dragged any place on the screen.  Use
18838      * this method to limit the horizontal travel of the element.  Pass in
18839      * 0,0 for the parameters if you want to lock the drag to the y axis.
18840      * @method setXConstraint
18841      * @param {int} iLeft the number of pixels the element can move to the left
18842      * @param {int} iRight the number of pixels the element can move to the
18843      * right
18844      * @param {int} iTickSize optional parameter for specifying that the
18845      * element
18846      * should move iTickSize pixels at a time.
18847      */
18848     setXConstraint: function(iLeft, iRight, iTickSize) {
18849         this.leftConstraint = iLeft;
18850         this.rightConstraint = iRight;
18851
18852         this.minX = this.initPageX - iLeft;
18853         this.maxX = this.initPageX + iRight;
18854         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18855
18856         this.constrainX = true;
18857     },
18858
18859     /**
18860      * Clears any constraints applied to this instance.  Also clears ticks
18861      * since they can't exist independent of a constraint at this time.
18862      * @method clearConstraints
18863      */
18864     clearConstraints: function() {
18865         this.constrainX = false;
18866         this.constrainY = false;
18867         this.clearTicks();
18868     },
18869
18870     /**
18871      * Clears any tick interval defined for this instance
18872      * @method clearTicks
18873      */
18874     clearTicks: function() {
18875         this.xTicks = null;
18876         this.yTicks = null;
18877         this.xTickSize = 0;
18878         this.yTickSize = 0;
18879     },
18880
18881     /**
18882      * By default, the element can be dragged any place on the screen.  Set
18883      * this to limit the vertical travel of the element.  Pass in 0,0 for the
18884      * parameters if you want to lock the drag to the x axis.
18885      * @method setYConstraint
18886      * @param {int} iUp the number of pixels the element can move up
18887      * @param {int} iDown the number of pixels the element can move down
18888      * @param {int} iTickSize optional parameter for specifying that the
18889      * element should move iTickSize pixels at a time.
18890      */
18891     setYConstraint: function(iUp, iDown, iTickSize) {
18892         this.topConstraint = iUp;
18893         this.bottomConstraint = iDown;
18894
18895         this.minY = this.initPageY - iUp;
18896         this.maxY = this.initPageY + iDown;
18897         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
18898
18899         this.constrainY = true;
18900
18901     },
18902
18903     /**
18904      * resetConstraints must be called if you manually reposition a dd element.
18905      * @method resetConstraints
18906      * @param {boolean} maintainOffset
18907      */
18908     resetConstraints: function() {
18909
18910
18911         // Maintain offsets if necessary
18912         if (this.initPageX || this.initPageX === 0) {
18913             // figure out how much this thing has moved
18914             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
18915             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
18916
18917             this.setInitPosition(dx, dy);
18918
18919         // This is the first time we have detected the element's position
18920         } else {
18921             this.setInitPosition();
18922         }
18923
18924         if (this.constrainX) {
18925             this.setXConstraint( this.leftConstraint,
18926                                  this.rightConstraint,
18927                                  this.xTickSize        );
18928         }
18929
18930         if (this.constrainY) {
18931             this.setYConstraint( this.topConstraint,
18932                                  this.bottomConstraint,
18933                                  this.yTickSize         );
18934         }
18935     },
18936
18937     /**
18938      * Normally the drag element is moved pixel by pixel, but we can specify
18939      * that it move a number of pixels at a time.  This method resolves the
18940      * location when we have it set up like this.
18941      * @method getTick
18942      * @param {int} val where we want to place the object
18943      * @param {int[]} tickArray sorted array of valid points
18944      * @return {int} the closest tick
18945      * @private
18946      */
18947     getTick: function(val, tickArray) {
18948
18949         if (!tickArray) {
18950             // If tick interval is not defined, it is effectively 1 pixel,
18951             // so we return the value passed to us.
18952             return val;
18953         } else if (tickArray[0] >= val) {
18954             // The value is lower than the first tick, so we return the first
18955             // tick.
18956             return tickArray[0];
18957         } else {
18958             for (var i=0, len=tickArray.length; i<len; ++i) {
18959                 var next = i + 1;
18960                 if (tickArray[next] && tickArray[next] >= val) {
18961                     var diff1 = val - tickArray[i];
18962                     var diff2 = tickArray[next] - val;
18963                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
18964                 }
18965             }
18966
18967             // The value is larger than the last tick, so we return the last
18968             // tick.
18969             return tickArray[tickArray.length - 1];
18970         }
18971     },
18972
18973     /**
18974      * toString method
18975      * @method toString
18976      * @return {string} string representation of the dd obj
18977      */
18978     toString: function() {
18979         return ("DragDrop " + this.id);
18980     }
18981
18982 });
18983
18984 })();
18985 /*
18986  * Based on:
18987  * Ext JS Library 1.1.1
18988  * Copyright(c) 2006-2007, Ext JS, LLC.
18989  *
18990  * Originally Released Under LGPL - original licence link has changed is not relivant.
18991  *
18992  * Fork - LGPL
18993  * <script type="text/javascript">
18994  */
18995
18996
18997 /**
18998  * The drag and drop utility provides a framework for building drag and drop
18999  * applications.  In addition to enabling drag and drop for specific elements,
19000  * the drag and drop elements are tracked by the manager class, and the
19001  * interactions between the various elements are tracked during the drag and
19002  * the implementing code is notified about these important moments.
19003  */
19004
19005 // Only load the library once.  Rewriting the manager class would orphan
19006 // existing drag and drop instances.
19007 if (!Roo.dd.DragDropMgr) {
19008
19009 /**
19010  * @class Roo.dd.DragDropMgr
19011  * DragDropMgr is a singleton that tracks the element interaction for
19012  * all DragDrop items in the window.  Generally, you will not call
19013  * this class directly, but it does have helper methods that could
19014  * be useful in your DragDrop implementations.
19015  * @singleton
19016  */
19017 Roo.dd.DragDropMgr = function() {
19018
19019     var Event = Roo.EventManager;
19020
19021     return {
19022
19023         /**
19024          * Two dimensional Array of registered DragDrop objects.  The first
19025          * dimension is the DragDrop item group, the second the DragDrop
19026          * object.
19027          * @property ids
19028          * @type {string: string}
19029          * @private
19030          * @static
19031          */
19032         ids: {},
19033
19034         /**
19035          * Array of element ids defined as drag handles.  Used to determine
19036          * if the element that generated the mousedown event is actually the
19037          * handle and not the html element itself.
19038          * @property handleIds
19039          * @type {string: string}
19040          * @private
19041          * @static
19042          */
19043         handleIds: {},
19044
19045         /**
19046          * the DragDrop object that is currently being dragged
19047          * @property dragCurrent
19048          * @type DragDrop
19049          * @private
19050          * @static
19051          **/
19052         dragCurrent: null,
19053
19054         /**
19055          * the DragDrop object(s) that are being hovered over
19056          * @property dragOvers
19057          * @type Array
19058          * @private
19059          * @static
19060          */
19061         dragOvers: {},
19062
19063         /**
19064          * the X distance between the cursor and the object being dragged
19065          * @property deltaX
19066          * @type int
19067          * @private
19068          * @static
19069          */
19070         deltaX: 0,
19071
19072         /**
19073          * the Y distance between the cursor and the object being dragged
19074          * @property deltaY
19075          * @type int
19076          * @private
19077          * @static
19078          */
19079         deltaY: 0,
19080
19081         /**
19082          * Flag to determine if we should prevent the default behavior of the
19083          * events we define. By default this is true, but this can be set to
19084          * false if you need the default behavior (not recommended)
19085          * @property preventDefault
19086          * @type boolean
19087          * @static
19088          */
19089         preventDefault: true,
19090
19091         /**
19092          * Flag to determine if we should stop the propagation of the events
19093          * we generate. This is true by default but you may want to set it to
19094          * false if the html element contains other features that require the
19095          * mouse click.
19096          * @property stopPropagation
19097          * @type boolean
19098          * @static
19099          */
19100         stopPropagation: true,
19101
19102         /**
19103          * Internal flag that is set to true when drag and drop has been
19104          * intialized
19105          * @property initialized
19106          * @private
19107          * @static
19108          */
19109         initalized: false,
19110
19111         /**
19112          * All drag and drop can be disabled.
19113          * @property locked
19114          * @private
19115          * @static
19116          */
19117         locked: false,
19118
19119         /**
19120          * Called the first time an element is registered.
19121          * @method init
19122          * @private
19123          * @static
19124          */
19125         init: function() {
19126             this.initialized = true;
19127         },
19128
19129         /**
19130          * In point mode, drag and drop interaction is defined by the
19131          * location of the cursor during the drag/drop
19132          * @property POINT
19133          * @type int
19134          * @static
19135          */
19136         POINT: 0,
19137
19138         /**
19139          * In intersect mode, drag and drop interactio nis defined by the
19140          * overlap of two or more drag and drop objects.
19141          * @property INTERSECT
19142          * @type int
19143          * @static
19144          */
19145         INTERSECT: 1,
19146
19147         /**
19148          * The current drag and drop mode.  Default: POINT
19149          * @property mode
19150          * @type int
19151          * @static
19152          */
19153         mode: 0,
19154
19155         /**
19156          * Runs method on all drag and drop objects
19157          * @method _execOnAll
19158          * @private
19159          * @static
19160          */
19161         _execOnAll: function(sMethod, args) {
19162             for (var i in this.ids) {
19163                 for (var j in this.ids[i]) {
19164                     var oDD = this.ids[i][j];
19165                     if (! this.isTypeOfDD(oDD)) {
19166                         continue;
19167                     }
19168                     oDD[sMethod].apply(oDD, args);
19169                 }
19170             }
19171         },
19172
19173         /**
19174          * Drag and drop initialization.  Sets up the global event handlers
19175          * @method _onLoad
19176          * @private
19177          * @static
19178          */
19179         _onLoad: function() {
19180
19181             this.init();
19182
19183             if (!Roo.isTouch) {
19184                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19185                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19186             }
19187             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19188             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19189             
19190             Event.on(window,   "unload",    this._onUnload, this, true);
19191             Event.on(window,   "resize",    this._onResize, this, true);
19192             // Event.on(window,   "mouseout",    this._test);
19193
19194         },
19195
19196         /**
19197          * Reset constraints on all drag and drop objs
19198          * @method _onResize
19199          * @private
19200          * @static
19201          */
19202         _onResize: function(e) {
19203             this._execOnAll("resetConstraints", []);
19204         },
19205
19206         /**
19207          * Lock all drag and drop functionality
19208          * @method lock
19209          * @static
19210          */
19211         lock: function() { this.locked = true; },
19212
19213         /**
19214          * Unlock all drag and drop functionality
19215          * @method unlock
19216          * @static
19217          */
19218         unlock: function() { this.locked = false; },
19219
19220         /**
19221          * Is drag and drop locked?
19222          * @method isLocked
19223          * @return {boolean} True if drag and drop is locked, false otherwise.
19224          * @static
19225          */
19226         isLocked: function() { return this.locked; },
19227
19228         /**
19229          * Location cache that is set for all drag drop objects when a drag is
19230          * initiated, cleared when the drag is finished.
19231          * @property locationCache
19232          * @private
19233          * @static
19234          */
19235         locationCache: {},
19236
19237         /**
19238          * Set useCache to false if you want to force object the lookup of each
19239          * drag and drop linked element constantly during a drag.
19240          * @property useCache
19241          * @type boolean
19242          * @static
19243          */
19244         useCache: true,
19245
19246         /**
19247          * The number of pixels that the mouse needs to move after the
19248          * mousedown before the drag is initiated.  Default=3;
19249          * @property clickPixelThresh
19250          * @type int
19251          * @static
19252          */
19253         clickPixelThresh: 3,
19254
19255         /**
19256          * The number of milliseconds after the mousedown event to initiate the
19257          * drag if we don't get a mouseup event. Default=1000
19258          * @property clickTimeThresh
19259          * @type int
19260          * @static
19261          */
19262         clickTimeThresh: 350,
19263
19264         /**
19265          * Flag that indicates that either the drag pixel threshold or the
19266          * mousdown time threshold has been met
19267          * @property dragThreshMet
19268          * @type boolean
19269          * @private
19270          * @static
19271          */
19272         dragThreshMet: false,
19273
19274         /**
19275          * Timeout used for the click time threshold
19276          * @property clickTimeout
19277          * @type Object
19278          * @private
19279          * @static
19280          */
19281         clickTimeout: null,
19282
19283         /**
19284          * The X position of the mousedown event stored for later use when a
19285          * drag threshold is met.
19286          * @property startX
19287          * @type int
19288          * @private
19289          * @static
19290          */
19291         startX: 0,
19292
19293         /**
19294          * The Y position of the mousedown event stored for later use when a
19295          * drag threshold is met.
19296          * @property startY
19297          * @type int
19298          * @private
19299          * @static
19300          */
19301         startY: 0,
19302
19303         /**
19304          * Each DragDrop instance must be registered with the DragDropMgr.
19305          * This is executed in DragDrop.init()
19306          * @method regDragDrop
19307          * @param {DragDrop} oDD the DragDrop object to register
19308          * @param {String} sGroup the name of the group this element belongs to
19309          * @static
19310          */
19311         regDragDrop: function(oDD, sGroup) {
19312             if (!this.initialized) { this.init(); }
19313
19314             if (!this.ids[sGroup]) {
19315                 this.ids[sGroup] = {};
19316             }
19317             this.ids[sGroup][oDD.id] = oDD;
19318         },
19319
19320         /**
19321          * Removes the supplied dd instance from the supplied group. Executed
19322          * by DragDrop.removeFromGroup, so don't call this function directly.
19323          * @method removeDDFromGroup
19324          * @private
19325          * @static
19326          */
19327         removeDDFromGroup: function(oDD, sGroup) {
19328             if (!this.ids[sGroup]) {
19329                 this.ids[sGroup] = {};
19330             }
19331
19332             var obj = this.ids[sGroup];
19333             if (obj && obj[oDD.id]) {
19334                 delete obj[oDD.id];
19335             }
19336         },
19337
19338         /**
19339          * Unregisters a drag and drop item.  This is executed in
19340          * DragDrop.unreg, use that method instead of calling this directly.
19341          * @method _remove
19342          * @private
19343          * @static
19344          */
19345         _remove: function(oDD) {
19346             for (var g in oDD.groups) {
19347                 if (g && this.ids[g][oDD.id]) {
19348                     delete this.ids[g][oDD.id];
19349                 }
19350             }
19351             delete this.handleIds[oDD.id];
19352         },
19353
19354         /**
19355          * Each DragDrop handle element must be registered.  This is done
19356          * automatically when executing DragDrop.setHandleElId()
19357          * @method regHandle
19358          * @param {String} sDDId the DragDrop id this element is a handle for
19359          * @param {String} sHandleId the id of the element that is the drag
19360          * handle
19361          * @static
19362          */
19363         regHandle: function(sDDId, sHandleId) {
19364             if (!this.handleIds[sDDId]) {
19365                 this.handleIds[sDDId] = {};
19366             }
19367             this.handleIds[sDDId][sHandleId] = sHandleId;
19368         },
19369
19370         /**
19371          * Utility function to determine if a given element has been
19372          * registered as a drag drop item.
19373          * @method isDragDrop
19374          * @param {String} id the element id to check
19375          * @return {boolean} true if this element is a DragDrop item,
19376          * false otherwise
19377          * @static
19378          */
19379         isDragDrop: function(id) {
19380             return ( this.getDDById(id) ) ? true : false;
19381         },
19382
19383         /**
19384          * Returns the drag and drop instances that are in all groups the
19385          * passed in instance belongs to.
19386          * @method getRelated
19387          * @param {DragDrop} p_oDD the obj to get related data for
19388          * @param {boolean} bTargetsOnly if true, only return targetable objs
19389          * @return {DragDrop[]} the related instances
19390          * @static
19391          */
19392         getRelated: function(p_oDD, bTargetsOnly) {
19393             var oDDs = [];
19394             for (var i in p_oDD.groups) {
19395                 for (j in this.ids[i]) {
19396                     var dd = this.ids[i][j];
19397                     if (! this.isTypeOfDD(dd)) {
19398                         continue;
19399                     }
19400                     if (!bTargetsOnly || dd.isTarget) {
19401                         oDDs[oDDs.length] = dd;
19402                     }
19403                 }
19404             }
19405
19406             return oDDs;
19407         },
19408
19409         /**
19410          * Returns true if the specified dd target is a legal target for
19411          * the specifice drag obj
19412          * @method isLegalTarget
19413          * @param {DragDrop} the drag obj
19414          * @param {DragDrop} the target
19415          * @return {boolean} true if the target is a legal target for the
19416          * dd obj
19417          * @static
19418          */
19419         isLegalTarget: function (oDD, oTargetDD) {
19420             var targets = this.getRelated(oDD, true);
19421             for (var i=0, len=targets.length;i<len;++i) {
19422                 if (targets[i].id == oTargetDD.id) {
19423                     return true;
19424                 }
19425             }
19426
19427             return false;
19428         },
19429
19430         /**
19431          * My goal is to be able to transparently determine if an object is
19432          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19433          * returns "object", oDD.constructor.toString() always returns
19434          * "DragDrop" and not the name of the subclass.  So for now it just
19435          * evaluates a well-known variable in DragDrop.
19436          * @method isTypeOfDD
19437          * @param {Object} the object to evaluate
19438          * @return {boolean} true if typeof oDD = DragDrop
19439          * @static
19440          */
19441         isTypeOfDD: function (oDD) {
19442             return (oDD && oDD.__ygDragDrop);
19443         },
19444
19445         /**
19446          * Utility function to determine if a given element has been
19447          * registered as a drag drop handle for the given Drag Drop object.
19448          * @method isHandle
19449          * @param {String} id the element id to check
19450          * @return {boolean} true if this element is a DragDrop handle, false
19451          * otherwise
19452          * @static
19453          */
19454         isHandle: function(sDDId, sHandleId) {
19455             return ( this.handleIds[sDDId] &&
19456                             this.handleIds[sDDId][sHandleId] );
19457         },
19458
19459         /**
19460          * Returns the DragDrop instance for a given id
19461          * @method getDDById
19462          * @param {String} id the id of the DragDrop object
19463          * @return {DragDrop} the drag drop object, null if it is not found
19464          * @static
19465          */
19466         getDDById: function(id) {
19467             for (var i in this.ids) {
19468                 if (this.ids[i][id]) {
19469                     return this.ids[i][id];
19470                 }
19471             }
19472             return null;
19473         },
19474
19475         /**
19476          * Fired after a registered DragDrop object gets the mousedown event.
19477          * Sets up the events required to track the object being dragged
19478          * @method handleMouseDown
19479          * @param {Event} e the event
19480          * @param oDD the DragDrop object being dragged
19481          * @private
19482          * @static
19483          */
19484         handleMouseDown: function(e, oDD) {
19485             if(Roo.QuickTips){
19486                 Roo.QuickTips.disable();
19487             }
19488             this.currentTarget = e.getTarget();
19489
19490             this.dragCurrent = oDD;
19491
19492             var el = oDD.getEl();
19493
19494             // track start position
19495             this.startX = e.getPageX();
19496             this.startY = e.getPageY();
19497
19498             this.deltaX = this.startX - el.offsetLeft;
19499             this.deltaY = this.startY - el.offsetTop;
19500
19501             this.dragThreshMet = false;
19502
19503             this.clickTimeout = setTimeout(
19504                     function() {
19505                         var DDM = Roo.dd.DDM;
19506                         DDM.startDrag(DDM.startX, DDM.startY);
19507                     },
19508                     this.clickTimeThresh );
19509         },
19510
19511         /**
19512          * Fired when either the drag pixel threshol or the mousedown hold
19513          * time threshold has been met.
19514          * @method startDrag
19515          * @param x {int} the X position of the original mousedown
19516          * @param y {int} the Y position of the original mousedown
19517          * @static
19518          */
19519         startDrag: function(x, y) {
19520             clearTimeout(this.clickTimeout);
19521             if (this.dragCurrent) {
19522                 this.dragCurrent.b4StartDrag(x, y);
19523                 this.dragCurrent.startDrag(x, y);
19524             }
19525             this.dragThreshMet = true;
19526         },
19527
19528         /**
19529          * Internal function to handle the mouseup event.  Will be invoked
19530          * from the context of the document.
19531          * @method handleMouseUp
19532          * @param {Event} e the event
19533          * @private
19534          * @static
19535          */
19536         handleMouseUp: function(e) {
19537
19538             if(Roo.QuickTips){
19539                 Roo.QuickTips.enable();
19540             }
19541             if (! this.dragCurrent) {
19542                 return;
19543             }
19544
19545             clearTimeout(this.clickTimeout);
19546
19547             if (this.dragThreshMet) {
19548                 this.fireEvents(e, true);
19549             } else {
19550             }
19551
19552             this.stopDrag(e);
19553
19554             this.stopEvent(e);
19555         },
19556
19557         /**
19558          * Utility to stop event propagation and event default, if these
19559          * features are turned on.
19560          * @method stopEvent
19561          * @param {Event} e the event as returned by this.getEvent()
19562          * @static
19563          */
19564         stopEvent: function(e){
19565             if(this.stopPropagation) {
19566                 e.stopPropagation();
19567             }
19568
19569             if (this.preventDefault) {
19570                 e.preventDefault();
19571             }
19572         },
19573
19574         /**
19575          * Internal function to clean up event handlers after the drag
19576          * operation is complete
19577          * @method stopDrag
19578          * @param {Event} e the event
19579          * @private
19580          * @static
19581          */
19582         stopDrag: function(e) {
19583             // Fire the drag end event for the item that was dragged
19584             if (this.dragCurrent) {
19585                 if (this.dragThreshMet) {
19586                     this.dragCurrent.b4EndDrag(e);
19587                     this.dragCurrent.endDrag(e);
19588                 }
19589
19590                 this.dragCurrent.onMouseUp(e);
19591             }
19592
19593             this.dragCurrent = null;
19594             this.dragOvers = {};
19595         },
19596
19597         /**
19598          * Internal function to handle the mousemove event.  Will be invoked
19599          * from the context of the html element.
19600          *
19601          * @TODO figure out what we can do about mouse events lost when the
19602          * user drags objects beyond the window boundary.  Currently we can
19603          * detect this in internet explorer by verifying that the mouse is
19604          * down during the mousemove event.  Firefox doesn't give us the
19605          * button state on the mousemove event.
19606          * @method handleMouseMove
19607          * @param {Event} e the event
19608          * @private
19609          * @static
19610          */
19611         handleMouseMove: function(e) {
19612             if (! this.dragCurrent) {
19613                 return true;
19614             }
19615
19616             // var button = e.which || e.button;
19617
19618             // check for IE mouseup outside of page boundary
19619             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19620                 this.stopEvent(e);
19621                 return this.handleMouseUp(e);
19622             }
19623
19624             if (!this.dragThreshMet) {
19625                 var diffX = Math.abs(this.startX - e.getPageX());
19626                 var diffY = Math.abs(this.startY - e.getPageY());
19627                 if (diffX > this.clickPixelThresh ||
19628                             diffY > this.clickPixelThresh) {
19629                     this.startDrag(this.startX, this.startY);
19630                 }
19631             }
19632
19633             if (this.dragThreshMet) {
19634                 this.dragCurrent.b4Drag(e);
19635                 this.dragCurrent.onDrag(e);
19636                 if(!this.dragCurrent.moveOnly){
19637                     this.fireEvents(e, false);
19638                 }
19639             }
19640
19641             this.stopEvent(e);
19642
19643             return true;
19644         },
19645
19646         /**
19647          * Iterates over all of the DragDrop elements to find ones we are
19648          * hovering over or dropping on
19649          * @method fireEvents
19650          * @param {Event} e the event
19651          * @param {boolean} isDrop is this a drop op or a mouseover op?
19652          * @private
19653          * @static
19654          */
19655         fireEvents: function(e, isDrop) {
19656             var dc = this.dragCurrent;
19657
19658             // If the user did the mouse up outside of the window, we could
19659             // get here even though we have ended the drag.
19660             if (!dc || dc.isLocked()) {
19661                 return;
19662             }
19663
19664             var pt = e.getPoint();
19665
19666             // cache the previous dragOver array
19667             var oldOvers = [];
19668
19669             var outEvts   = [];
19670             var overEvts  = [];
19671             var dropEvts  = [];
19672             var enterEvts = [];
19673
19674             // Check to see if the object(s) we were hovering over is no longer
19675             // being hovered over so we can fire the onDragOut event
19676             for (var i in this.dragOvers) {
19677
19678                 var ddo = this.dragOvers[i];
19679
19680                 if (! this.isTypeOfDD(ddo)) {
19681                     continue;
19682                 }
19683
19684                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19685                     outEvts.push( ddo );
19686                 }
19687
19688                 oldOvers[i] = true;
19689                 delete this.dragOvers[i];
19690             }
19691
19692             for (var sGroup in dc.groups) {
19693
19694                 if ("string" != typeof sGroup) {
19695                     continue;
19696                 }
19697
19698                 for (i in this.ids[sGroup]) {
19699                     var oDD = this.ids[sGroup][i];
19700                     if (! this.isTypeOfDD(oDD)) {
19701                         continue;
19702                     }
19703
19704                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19705                         if (this.isOverTarget(pt, oDD, this.mode)) {
19706                             // look for drop interactions
19707                             if (isDrop) {
19708                                 dropEvts.push( oDD );
19709                             // look for drag enter and drag over interactions
19710                             } else {
19711
19712                                 // initial drag over: dragEnter fires
19713                                 if (!oldOvers[oDD.id]) {
19714                                     enterEvts.push( oDD );
19715                                 // subsequent drag overs: dragOver fires
19716                                 } else {
19717                                     overEvts.push( oDD );
19718                                 }
19719
19720                                 this.dragOvers[oDD.id] = oDD;
19721                             }
19722                         }
19723                     }
19724                 }
19725             }
19726
19727             if (this.mode) {
19728                 if (outEvts.length) {
19729                     dc.b4DragOut(e, outEvts);
19730                     dc.onDragOut(e, outEvts);
19731                 }
19732
19733                 if (enterEvts.length) {
19734                     dc.onDragEnter(e, enterEvts);
19735                 }
19736
19737                 if (overEvts.length) {
19738                     dc.b4DragOver(e, overEvts);
19739                     dc.onDragOver(e, overEvts);
19740                 }
19741
19742                 if (dropEvts.length) {
19743                     dc.b4DragDrop(e, dropEvts);
19744                     dc.onDragDrop(e, dropEvts);
19745                 }
19746
19747             } else {
19748                 // fire dragout events
19749                 var len = 0;
19750                 for (i=0, len=outEvts.length; i<len; ++i) {
19751                     dc.b4DragOut(e, outEvts[i].id);
19752                     dc.onDragOut(e, outEvts[i].id);
19753                 }
19754
19755                 // fire enter events
19756                 for (i=0,len=enterEvts.length; i<len; ++i) {
19757                     // dc.b4DragEnter(e, oDD.id);
19758                     dc.onDragEnter(e, enterEvts[i].id);
19759                 }
19760
19761                 // fire over events
19762                 for (i=0,len=overEvts.length; i<len; ++i) {
19763                     dc.b4DragOver(e, overEvts[i].id);
19764                     dc.onDragOver(e, overEvts[i].id);
19765                 }
19766
19767                 // fire drop events
19768                 for (i=0, len=dropEvts.length; i<len; ++i) {
19769                     dc.b4DragDrop(e, dropEvts[i].id);
19770                     dc.onDragDrop(e, dropEvts[i].id);
19771                 }
19772
19773             }
19774
19775             // notify about a drop that did not find a target
19776             if (isDrop && !dropEvts.length) {
19777                 dc.onInvalidDrop(e);
19778             }
19779
19780         },
19781
19782         /**
19783          * Helper function for getting the best match from the list of drag
19784          * and drop objects returned by the drag and drop events when we are
19785          * in INTERSECT mode.  It returns either the first object that the
19786          * cursor is over, or the object that has the greatest overlap with
19787          * the dragged element.
19788          * @method getBestMatch
19789          * @param  {DragDrop[]} dds The array of drag and drop objects
19790          * targeted
19791          * @return {DragDrop}       The best single match
19792          * @static
19793          */
19794         getBestMatch: function(dds) {
19795             var winner = null;
19796             // Return null if the input is not what we expect
19797             //if (!dds || !dds.length || dds.length == 0) {
19798                // winner = null;
19799             // If there is only one item, it wins
19800             //} else if (dds.length == 1) {
19801
19802             var len = dds.length;
19803
19804             if (len == 1) {
19805                 winner = dds[0];
19806             } else {
19807                 // Loop through the targeted items
19808                 for (var i=0; i<len; ++i) {
19809                     var dd = dds[i];
19810                     // If the cursor is over the object, it wins.  If the
19811                     // cursor is over multiple matches, the first one we come
19812                     // to wins.
19813                     if (dd.cursorIsOver) {
19814                         winner = dd;
19815                         break;
19816                     // Otherwise the object with the most overlap wins
19817                     } else {
19818                         if (!winner ||
19819                             winner.overlap.getArea() < dd.overlap.getArea()) {
19820                             winner = dd;
19821                         }
19822                     }
19823                 }
19824             }
19825
19826             return winner;
19827         },
19828
19829         /**
19830          * Refreshes the cache of the top-left and bottom-right points of the
19831          * drag and drop objects in the specified group(s).  This is in the
19832          * format that is stored in the drag and drop instance, so typical
19833          * usage is:
19834          * <code>
19835          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19836          * </code>
19837          * Alternatively:
19838          * <code>
19839          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19840          * </code>
19841          * @TODO this really should be an indexed array.  Alternatively this
19842          * method could accept both.
19843          * @method refreshCache
19844          * @param {Object} groups an associative array of groups to refresh
19845          * @static
19846          */
19847         refreshCache: function(groups) {
19848             for (var sGroup in groups) {
19849                 if ("string" != typeof sGroup) {
19850                     continue;
19851                 }
19852                 for (var i in this.ids[sGroup]) {
19853                     var oDD = this.ids[sGroup][i];
19854
19855                     if (this.isTypeOfDD(oDD)) {
19856                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19857                         var loc = this.getLocation(oDD);
19858                         if (loc) {
19859                             this.locationCache[oDD.id] = loc;
19860                         } else {
19861                             delete this.locationCache[oDD.id];
19862                             // this will unregister the drag and drop object if
19863                             // the element is not in a usable state
19864                             // oDD.unreg();
19865                         }
19866                     }
19867                 }
19868             }
19869         },
19870
19871         /**
19872          * This checks to make sure an element exists and is in the DOM.  The
19873          * main purpose is to handle cases where innerHTML is used to remove
19874          * drag and drop objects from the DOM.  IE provides an 'unspecified
19875          * error' when trying to access the offsetParent of such an element
19876          * @method verifyEl
19877          * @param {HTMLElement} el the element to check
19878          * @return {boolean} true if the element looks usable
19879          * @static
19880          */
19881         verifyEl: function(el) {
19882             if (el) {
19883                 var parent;
19884                 if(Roo.isIE){
19885                     try{
19886                         parent = el.offsetParent;
19887                     }catch(e){}
19888                 }else{
19889                     parent = el.offsetParent;
19890                 }
19891                 if (parent) {
19892                     return true;
19893                 }
19894             }
19895
19896             return false;
19897         },
19898
19899         /**
19900          * Returns a Region object containing the drag and drop element's position
19901          * and size, including the padding configured for it
19902          * @method getLocation
19903          * @param {DragDrop} oDD the drag and drop object to get the
19904          *                       location for
19905          * @return {Roo.lib.Region} a Region object representing the total area
19906          *                             the element occupies, including any padding
19907          *                             the instance is configured for.
19908          * @static
19909          */
19910         getLocation: function(oDD) {
19911             if (! this.isTypeOfDD(oDD)) {
19912                 return null;
19913             }
19914
19915             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
19916
19917             try {
19918                 pos= Roo.lib.Dom.getXY(el);
19919             } catch (e) { }
19920
19921             if (!pos) {
19922                 return null;
19923             }
19924
19925             x1 = pos[0];
19926             x2 = x1 + el.offsetWidth;
19927             y1 = pos[1];
19928             y2 = y1 + el.offsetHeight;
19929
19930             t = y1 - oDD.padding[0];
19931             r = x2 + oDD.padding[1];
19932             b = y2 + oDD.padding[2];
19933             l = x1 - oDD.padding[3];
19934
19935             return new Roo.lib.Region( t, r, b, l );
19936         },
19937
19938         /**
19939          * Checks the cursor location to see if it over the target
19940          * @method isOverTarget
19941          * @param {Roo.lib.Point} pt The point to evaluate
19942          * @param {DragDrop} oTarget the DragDrop object we are inspecting
19943          * @return {boolean} true if the mouse is over the target
19944          * @private
19945          * @static
19946          */
19947         isOverTarget: function(pt, oTarget, intersect) {
19948             // use cache if available
19949             var loc = this.locationCache[oTarget.id];
19950             if (!loc || !this.useCache) {
19951                 loc = this.getLocation(oTarget);
19952                 this.locationCache[oTarget.id] = loc;
19953
19954             }
19955
19956             if (!loc) {
19957                 return false;
19958             }
19959
19960             oTarget.cursorIsOver = loc.contains( pt );
19961
19962             // DragDrop is using this as a sanity check for the initial mousedown
19963             // in this case we are done.  In POINT mode, if the drag obj has no
19964             // contraints, we are also done. Otherwise we need to evaluate the
19965             // location of the target as related to the actual location of the
19966             // dragged element.
19967             var dc = this.dragCurrent;
19968             if (!dc || !dc.getTargetCoord ||
19969                     (!intersect && !dc.constrainX && !dc.constrainY)) {
19970                 return oTarget.cursorIsOver;
19971             }
19972
19973             oTarget.overlap = null;
19974
19975             // Get the current location of the drag element, this is the
19976             // location of the mouse event less the delta that represents
19977             // where the original mousedown happened on the element.  We
19978             // need to consider constraints and ticks as well.
19979             var pos = dc.getTargetCoord(pt.x, pt.y);
19980
19981             var el = dc.getDragEl();
19982             var curRegion = new Roo.lib.Region( pos.y,
19983                                                    pos.x + el.offsetWidth,
19984                                                    pos.y + el.offsetHeight,
19985                                                    pos.x );
19986
19987             var overlap = curRegion.intersect(loc);
19988
19989             if (overlap) {
19990                 oTarget.overlap = overlap;
19991                 return (intersect) ? true : oTarget.cursorIsOver;
19992             } else {
19993                 return false;
19994             }
19995         },
19996
19997         /**
19998          * unload event handler
19999          * @method _onUnload
20000          * @private
20001          * @static
20002          */
20003         _onUnload: function(e, me) {
20004             Roo.dd.DragDropMgr.unregAll();
20005         },
20006
20007         /**
20008          * Cleans up the drag and drop events and objects.
20009          * @method unregAll
20010          * @private
20011          * @static
20012          */
20013         unregAll: function() {
20014
20015             if (this.dragCurrent) {
20016                 this.stopDrag();
20017                 this.dragCurrent = null;
20018             }
20019
20020             this._execOnAll("unreg", []);
20021
20022             for (i in this.elementCache) {
20023                 delete this.elementCache[i];
20024             }
20025
20026             this.elementCache = {};
20027             this.ids = {};
20028         },
20029
20030         /**
20031          * A cache of DOM elements
20032          * @property elementCache
20033          * @private
20034          * @static
20035          */
20036         elementCache: {},
20037
20038         /**
20039          * Get the wrapper for the DOM element specified
20040          * @method getElWrapper
20041          * @param {String} id the id of the element to get
20042          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20043          * @private
20044          * @deprecated This wrapper isn't that useful
20045          * @static
20046          */
20047         getElWrapper: function(id) {
20048             var oWrapper = this.elementCache[id];
20049             if (!oWrapper || !oWrapper.el) {
20050                 oWrapper = this.elementCache[id] =
20051                     new this.ElementWrapper(Roo.getDom(id));
20052             }
20053             return oWrapper;
20054         },
20055
20056         /**
20057          * Returns the actual DOM element
20058          * @method getElement
20059          * @param {String} id the id of the elment to get
20060          * @return {Object} The element
20061          * @deprecated use Roo.getDom instead
20062          * @static
20063          */
20064         getElement: function(id) {
20065             return Roo.getDom(id);
20066         },
20067
20068         /**
20069          * Returns the style property for the DOM element (i.e.,
20070          * document.getElById(id).style)
20071          * @method getCss
20072          * @param {String} id the id of the elment to get
20073          * @return {Object} The style property of the element
20074          * @deprecated use Roo.getDom instead
20075          * @static
20076          */
20077         getCss: function(id) {
20078             var el = Roo.getDom(id);
20079             return (el) ? el.style : null;
20080         },
20081
20082         /**
20083          * Inner class for cached elements
20084          * @class DragDropMgr.ElementWrapper
20085          * @for DragDropMgr
20086          * @private
20087          * @deprecated
20088          */
20089         ElementWrapper: function(el) {
20090                 /**
20091                  * The element
20092                  * @property el
20093                  */
20094                 this.el = el || null;
20095                 /**
20096                  * The element id
20097                  * @property id
20098                  */
20099                 this.id = this.el && el.id;
20100                 /**
20101                  * A reference to the style property
20102                  * @property css
20103                  */
20104                 this.css = this.el && el.style;
20105             },
20106
20107         /**
20108          * Returns the X position of an html element
20109          * @method getPosX
20110          * @param el the element for which to get the position
20111          * @return {int} the X coordinate
20112          * @for DragDropMgr
20113          * @deprecated use Roo.lib.Dom.getX instead
20114          * @static
20115          */
20116         getPosX: function(el) {
20117             return Roo.lib.Dom.getX(el);
20118         },
20119
20120         /**
20121          * Returns the Y position of an html element
20122          * @method getPosY
20123          * @param el the element for which to get the position
20124          * @return {int} the Y coordinate
20125          * @deprecated use Roo.lib.Dom.getY instead
20126          * @static
20127          */
20128         getPosY: function(el) {
20129             return Roo.lib.Dom.getY(el);
20130         },
20131
20132         /**
20133          * Swap two nodes.  In IE, we use the native method, for others we
20134          * emulate the IE behavior
20135          * @method swapNode
20136          * @param n1 the first node to swap
20137          * @param n2 the other node to swap
20138          * @static
20139          */
20140         swapNode: function(n1, n2) {
20141             if (n1.swapNode) {
20142                 n1.swapNode(n2);
20143             } else {
20144                 var p = n2.parentNode;
20145                 var s = n2.nextSibling;
20146
20147                 if (s == n1) {
20148                     p.insertBefore(n1, n2);
20149                 } else if (n2 == n1.nextSibling) {
20150                     p.insertBefore(n2, n1);
20151                 } else {
20152                     n1.parentNode.replaceChild(n2, n1);
20153                     p.insertBefore(n1, s);
20154                 }
20155             }
20156         },
20157
20158         /**
20159          * Returns the current scroll position
20160          * @method getScroll
20161          * @private
20162          * @static
20163          */
20164         getScroll: function () {
20165             var t, l, dde=document.documentElement, db=document.body;
20166             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20167                 t = dde.scrollTop;
20168                 l = dde.scrollLeft;
20169             } else if (db) {
20170                 t = db.scrollTop;
20171                 l = db.scrollLeft;
20172             } else {
20173
20174             }
20175             return { top: t, left: l };
20176         },
20177
20178         /**
20179          * Returns the specified element style property
20180          * @method getStyle
20181          * @param {HTMLElement} el          the element
20182          * @param {string}      styleProp   the style property
20183          * @return {string} The value of the style property
20184          * @deprecated use Roo.lib.Dom.getStyle
20185          * @static
20186          */
20187         getStyle: function(el, styleProp) {
20188             return Roo.fly(el).getStyle(styleProp);
20189         },
20190
20191         /**
20192          * Gets the scrollTop
20193          * @method getScrollTop
20194          * @return {int} the document's scrollTop
20195          * @static
20196          */
20197         getScrollTop: function () { return this.getScroll().top; },
20198
20199         /**
20200          * Gets the scrollLeft
20201          * @method getScrollLeft
20202          * @return {int} the document's scrollTop
20203          * @static
20204          */
20205         getScrollLeft: function () { return this.getScroll().left; },
20206
20207         /**
20208          * Sets the x/y position of an element to the location of the
20209          * target element.
20210          * @method moveToEl
20211          * @param {HTMLElement} moveEl      The element to move
20212          * @param {HTMLElement} targetEl    The position reference element
20213          * @static
20214          */
20215         moveToEl: function (moveEl, targetEl) {
20216             var aCoord = Roo.lib.Dom.getXY(targetEl);
20217             Roo.lib.Dom.setXY(moveEl, aCoord);
20218         },
20219
20220         /**
20221          * Numeric array sort function
20222          * @method numericSort
20223          * @static
20224          */
20225         numericSort: function(a, b) { return (a - b); },
20226
20227         /**
20228          * Internal counter
20229          * @property _timeoutCount
20230          * @private
20231          * @static
20232          */
20233         _timeoutCount: 0,
20234
20235         /**
20236          * Trying to make the load order less important.  Without this we get
20237          * an error if this file is loaded before the Event Utility.
20238          * @method _addListeners
20239          * @private
20240          * @static
20241          */
20242         _addListeners: function() {
20243             var DDM = Roo.dd.DDM;
20244             if ( Roo.lib.Event && document ) {
20245                 DDM._onLoad();
20246             } else {
20247                 if (DDM._timeoutCount > 2000) {
20248                 } else {
20249                     setTimeout(DDM._addListeners, 10);
20250                     if (document && document.body) {
20251                         DDM._timeoutCount += 1;
20252                     }
20253                 }
20254             }
20255         },
20256
20257         /**
20258          * Recursively searches the immediate parent and all child nodes for
20259          * the handle element in order to determine wheter or not it was
20260          * clicked.
20261          * @method handleWasClicked
20262          * @param node the html element to inspect
20263          * @static
20264          */
20265         handleWasClicked: function(node, id) {
20266             if (this.isHandle(id, node.id)) {
20267                 return true;
20268             } else {
20269                 // check to see if this is a text node child of the one we want
20270                 var p = node.parentNode;
20271
20272                 while (p) {
20273                     if (this.isHandle(id, p.id)) {
20274                         return true;
20275                     } else {
20276                         p = p.parentNode;
20277                     }
20278                 }
20279             }
20280
20281             return false;
20282         }
20283
20284     };
20285
20286 }();
20287
20288 // shorter alias, save a few bytes
20289 Roo.dd.DDM = Roo.dd.DragDropMgr;
20290 Roo.dd.DDM._addListeners();
20291
20292 }/*
20293  * Based on:
20294  * Ext JS Library 1.1.1
20295  * Copyright(c) 2006-2007, Ext JS, LLC.
20296  *
20297  * Originally Released Under LGPL - original licence link has changed is not relivant.
20298  *
20299  * Fork - LGPL
20300  * <script type="text/javascript">
20301  */
20302
20303 /**
20304  * @class Roo.dd.DD
20305  * A DragDrop implementation where the linked element follows the
20306  * mouse cursor during a drag.
20307  * @extends Roo.dd.DragDrop
20308  * @constructor
20309  * @param {String} id the id of the linked element
20310  * @param {String} sGroup the group of related DragDrop items
20311  * @param {object} config an object containing configurable attributes
20312  *                Valid properties for DD:
20313  *                    scroll
20314  */
20315 Roo.dd.DD = function(id, sGroup, config) {
20316     if (id) {
20317         this.init(id, sGroup, config);
20318     }
20319 };
20320
20321 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20322
20323     /**
20324      * When set to true, the utility automatically tries to scroll the browser
20325      * window wehn a drag and drop element is dragged near the viewport boundary.
20326      * Defaults to true.
20327      * @property scroll
20328      * @type boolean
20329      */
20330     scroll: true,
20331
20332     /**
20333      * Sets the pointer offset to the distance between the linked element's top
20334      * left corner and the location the element was clicked
20335      * @method autoOffset
20336      * @param {int} iPageX the X coordinate of the click
20337      * @param {int} iPageY the Y coordinate of the click
20338      */
20339     autoOffset: function(iPageX, iPageY) {
20340         var x = iPageX - this.startPageX;
20341         var y = iPageY - this.startPageY;
20342         this.setDelta(x, y);
20343     },
20344
20345     /**
20346      * Sets the pointer offset.  You can call this directly to force the
20347      * offset to be in a particular location (e.g., pass in 0,0 to set it
20348      * to the center of the object)
20349      * @method setDelta
20350      * @param {int} iDeltaX the distance from the left
20351      * @param {int} iDeltaY the distance from the top
20352      */
20353     setDelta: function(iDeltaX, iDeltaY) {
20354         this.deltaX = iDeltaX;
20355         this.deltaY = iDeltaY;
20356     },
20357
20358     /**
20359      * Sets the drag element to the location of the mousedown or click event,
20360      * maintaining the cursor location relative to the location on the element
20361      * that was clicked.  Override this if you want to place the element in a
20362      * location other than where the cursor is.
20363      * @method setDragElPos
20364      * @param {int} iPageX the X coordinate of the mousedown or drag event
20365      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20366      */
20367     setDragElPos: function(iPageX, iPageY) {
20368         // the first time we do this, we are going to check to make sure
20369         // the element has css positioning
20370
20371         var el = this.getDragEl();
20372         this.alignElWithMouse(el, iPageX, iPageY);
20373     },
20374
20375     /**
20376      * Sets the element to the location of the mousedown or click event,
20377      * maintaining the cursor location relative to the location on the element
20378      * that was clicked.  Override this if you want to place the element in a
20379      * location other than where the cursor is.
20380      * @method alignElWithMouse
20381      * @param {HTMLElement} el the element to move
20382      * @param {int} iPageX the X coordinate of the mousedown or drag event
20383      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20384      */
20385     alignElWithMouse: function(el, iPageX, iPageY) {
20386         var oCoord = this.getTargetCoord(iPageX, iPageY);
20387         var fly = el.dom ? el : Roo.fly(el);
20388         if (!this.deltaSetXY) {
20389             var aCoord = [oCoord.x, oCoord.y];
20390             fly.setXY(aCoord);
20391             var newLeft = fly.getLeft(true);
20392             var newTop  = fly.getTop(true);
20393             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20394         } else {
20395             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20396         }
20397
20398         this.cachePosition(oCoord.x, oCoord.y);
20399         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20400         return oCoord;
20401     },
20402
20403     /**
20404      * Saves the most recent position so that we can reset the constraints and
20405      * tick marks on-demand.  We need to know this so that we can calculate the
20406      * number of pixels the element is offset from its original position.
20407      * @method cachePosition
20408      * @param iPageX the current x position (optional, this just makes it so we
20409      * don't have to look it up again)
20410      * @param iPageY the current y position (optional, this just makes it so we
20411      * don't have to look it up again)
20412      */
20413     cachePosition: function(iPageX, iPageY) {
20414         if (iPageX) {
20415             this.lastPageX = iPageX;
20416             this.lastPageY = iPageY;
20417         } else {
20418             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20419             this.lastPageX = aCoord[0];
20420             this.lastPageY = aCoord[1];
20421         }
20422     },
20423
20424     /**
20425      * Auto-scroll the window if the dragged object has been moved beyond the
20426      * visible window boundary.
20427      * @method autoScroll
20428      * @param {int} x the drag element's x position
20429      * @param {int} y the drag element's y position
20430      * @param {int} h the height of the drag element
20431      * @param {int} w the width of the drag element
20432      * @private
20433      */
20434     autoScroll: function(x, y, h, w) {
20435
20436         if (this.scroll) {
20437             // The client height
20438             var clientH = Roo.lib.Dom.getViewWidth();
20439
20440             // The client width
20441             var clientW = Roo.lib.Dom.getViewHeight();
20442
20443             // The amt scrolled down
20444             var st = this.DDM.getScrollTop();
20445
20446             // The amt scrolled right
20447             var sl = this.DDM.getScrollLeft();
20448
20449             // Location of the bottom of the element
20450             var bot = h + y;
20451
20452             // Location of the right of the element
20453             var right = w + x;
20454
20455             // The distance from the cursor to the bottom of the visible area,
20456             // adjusted so that we don't scroll if the cursor is beyond the
20457             // element drag constraints
20458             var toBot = (clientH + st - y - this.deltaY);
20459
20460             // The distance from the cursor to the right of the visible area
20461             var toRight = (clientW + sl - x - this.deltaX);
20462
20463
20464             // How close to the edge the cursor must be before we scroll
20465             // var thresh = (document.all) ? 100 : 40;
20466             var thresh = 40;
20467
20468             // How many pixels to scroll per autoscroll op.  This helps to reduce
20469             // clunky scrolling. IE is more sensitive about this ... it needs this
20470             // value to be higher.
20471             var scrAmt = (document.all) ? 80 : 30;
20472
20473             // Scroll down if we are near the bottom of the visible page and the
20474             // obj extends below the crease
20475             if ( bot > clientH && toBot < thresh ) {
20476                 window.scrollTo(sl, st + scrAmt);
20477             }
20478
20479             // Scroll up if the window is scrolled down and the top of the object
20480             // goes above the top border
20481             if ( y < st && st > 0 && y - st < thresh ) {
20482                 window.scrollTo(sl, st - scrAmt);
20483             }
20484
20485             // Scroll right if the obj is beyond the right border and the cursor is
20486             // near the border.
20487             if ( right > clientW && toRight < thresh ) {
20488                 window.scrollTo(sl + scrAmt, st);
20489             }
20490
20491             // Scroll left if the window has been scrolled to the right and the obj
20492             // extends past the left border
20493             if ( x < sl && sl > 0 && x - sl < thresh ) {
20494                 window.scrollTo(sl - scrAmt, st);
20495             }
20496         }
20497     },
20498
20499     /**
20500      * Finds the location the element should be placed if we want to move
20501      * it to where the mouse location less the click offset would place us.
20502      * @method getTargetCoord
20503      * @param {int} iPageX the X coordinate of the click
20504      * @param {int} iPageY the Y coordinate of the click
20505      * @return an object that contains the coordinates (Object.x and Object.y)
20506      * @private
20507      */
20508     getTargetCoord: function(iPageX, iPageY) {
20509
20510
20511         var x = iPageX - this.deltaX;
20512         var y = iPageY - this.deltaY;
20513
20514         if (this.constrainX) {
20515             if (x < this.minX) { x = this.minX; }
20516             if (x > this.maxX) { x = this.maxX; }
20517         }
20518
20519         if (this.constrainY) {
20520             if (y < this.minY) { y = this.minY; }
20521             if (y > this.maxY) { y = this.maxY; }
20522         }
20523
20524         x = this.getTick(x, this.xTicks);
20525         y = this.getTick(y, this.yTicks);
20526
20527
20528         return {x:x, y:y};
20529     },
20530
20531     /*
20532      * Sets up config options specific to this class. Overrides
20533      * Roo.dd.DragDrop, but all versions of this method through the
20534      * inheritance chain are called
20535      */
20536     applyConfig: function() {
20537         Roo.dd.DD.superclass.applyConfig.call(this);
20538         this.scroll = (this.config.scroll !== false);
20539     },
20540
20541     /*
20542      * Event that fires prior to the onMouseDown event.  Overrides
20543      * Roo.dd.DragDrop.
20544      */
20545     b4MouseDown: function(e) {
20546         // this.resetConstraints();
20547         this.autoOffset(e.getPageX(),
20548                             e.getPageY());
20549     },
20550
20551     /*
20552      * Event that fires prior to the onDrag event.  Overrides
20553      * Roo.dd.DragDrop.
20554      */
20555     b4Drag: function(e) {
20556         this.setDragElPos(e.getPageX(),
20557                             e.getPageY());
20558     },
20559
20560     toString: function() {
20561         return ("DD " + this.id);
20562     }
20563
20564     //////////////////////////////////////////////////////////////////////////
20565     // Debugging ygDragDrop events that can be overridden
20566     //////////////////////////////////////////////////////////////////////////
20567     /*
20568     startDrag: function(x, y) {
20569     },
20570
20571     onDrag: function(e) {
20572     },
20573
20574     onDragEnter: function(e, id) {
20575     },
20576
20577     onDragOver: function(e, id) {
20578     },
20579
20580     onDragOut: function(e, id) {
20581     },
20582
20583     onDragDrop: function(e, id) {
20584     },
20585
20586     endDrag: function(e) {
20587     }
20588
20589     */
20590
20591 });/*
20592  * Based on:
20593  * Ext JS Library 1.1.1
20594  * Copyright(c) 2006-2007, Ext JS, LLC.
20595  *
20596  * Originally Released Under LGPL - original licence link has changed is not relivant.
20597  *
20598  * Fork - LGPL
20599  * <script type="text/javascript">
20600  */
20601
20602 /**
20603  * @class Roo.dd.DDProxy
20604  * A DragDrop implementation that inserts an empty, bordered div into
20605  * the document that follows the cursor during drag operations.  At the time of
20606  * the click, the frame div is resized to the dimensions of the linked html
20607  * element, and moved to the exact location of the linked element.
20608  *
20609  * References to the "frame" element refer to the single proxy element that
20610  * was created to be dragged in place of all DDProxy elements on the
20611  * page.
20612  *
20613  * @extends Roo.dd.DD
20614  * @constructor
20615  * @param {String} id the id of the linked html element
20616  * @param {String} sGroup the group of related DragDrop objects
20617  * @param {object} config an object containing configurable attributes
20618  *                Valid properties for DDProxy in addition to those in DragDrop:
20619  *                   resizeFrame, centerFrame, dragElId
20620  */
20621 Roo.dd.DDProxy = function(id, sGroup, config) {
20622     if (id) {
20623         this.init(id, sGroup, config);
20624         this.initFrame();
20625     }
20626 };
20627
20628 /**
20629  * The default drag frame div id
20630  * @property Roo.dd.DDProxy.dragElId
20631  * @type String
20632  * @static
20633  */
20634 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20635
20636 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20637
20638     /**
20639      * By default we resize the drag frame to be the same size as the element
20640      * we want to drag (this is to get the frame effect).  We can turn it off
20641      * if we want a different behavior.
20642      * @property resizeFrame
20643      * @type boolean
20644      */
20645     resizeFrame: true,
20646
20647     /**
20648      * By default the frame is positioned exactly where the drag element is, so
20649      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20650      * you do not have constraints on the obj is to have the drag frame centered
20651      * around the cursor.  Set centerFrame to true for this effect.
20652      * @property centerFrame
20653      * @type boolean
20654      */
20655     centerFrame: false,
20656
20657     /**
20658      * Creates the proxy element if it does not yet exist
20659      * @method createFrame
20660      */
20661     createFrame: function() {
20662         var self = this;
20663         var body = document.body;
20664
20665         if (!body || !body.firstChild) {
20666             setTimeout( function() { self.createFrame(); }, 50 );
20667             return;
20668         }
20669
20670         var div = this.getDragEl();
20671
20672         if (!div) {
20673             div    = document.createElement("div");
20674             div.id = this.dragElId;
20675             var s  = div.style;
20676
20677             s.position   = "absolute";
20678             s.visibility = "hidden";
20679             s.cursor     = "move";
20680             s.border     = "2px solid #aaa";
20681             s.zIndex     = 999;
20682
20683             // appendChild can blow up IE if invoked prior to the window load event
20684             // while rendering a table.  It is possible there are other scenarios
20685             // that would cause this to happen as well.
20686             body.insertBefore(div, body.firstChild);
20687         }
20688     },
20689
20690     /**
20691      * Initialization for the drag frame element.  Must be called in the
20692      * constructor of all subclasses
20693      * @method initFrame
20694      */
20695     initFrame: function() {
20696         this.createFrame();
20697     },
20698
20699     applyConfig: function() {
20700         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20701
20702         this.resizeFrame = (this.config.resizeFrame !== false);
20703         this.centerFrame = (this.config.centerFrame);
20704         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20705     },
20706
20707     /**
20708      * Resizes the drag frame to the dimensions of the clicked object, positions
20709      * it over the object, and finally displays it
20710      * @method showFrame
20711      * @param {int} iPageX X click position
20712      * @param {int} iPageY Y click position
20713      * @private
20714      */
20715     showFrame: function(iPageX, iPageY) {
20716         var el = this.getEl();
20717         var dragEl = this.getDragEl();
20718         var s = dragEl.style;
20719
20720         this._resizeProxy();
20721
20722         if (this.centerFrame) {
20723             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20724                            Math.round(parseInt(s.height, 10)/2) );
20725         }
20726
20727         this.setDragElPos(iPageX, iPageY);
20728
20729         Roo.fly(dragEl).show();
20730     },
20731
20732     /**
20733      * The proxy is automatically resized to the dimensions of the linked
20734      * element when a drag is initiated, unless resizeFrame is set to false
20735      * @method _resizeProxy
20736      * @private
20737      */
20738     _resizeProxy: function() {
20739         if (this.resizeFrame) {
20740             var el = this.getEl();
20741             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20742         }
20743     },
20744
20745     // overrides Roo.dd.DragDrop
20746     b4MouseDown: function(e) {
20747         var x = e.getPageX();
20748         var y = e.getPageY();
20749         this.autoOffset(x, y);
20750         this.setDragElPos(x, y);
20751     },
20752
20753     // overrides Roo.dd.DragDrop
20754     b4StartDrag: function(x, y) {
20755         // show the drag frame
20756         this.showFrame(x, y);
20757     },
20758
20759     // overrides Roo.dd.DragDrop
20760     b4EndDrag: function(e) {
20761         Roo.fly(this.getDragEl()).hide();
20762     },
20763
20764     // overrides Roo.dd.DragDrop
20765     // By default we try to move the element to the last location of the frame.
20766     // This is so that the default behavior mirrors that of Roo.dd.DD.
20767     endDrag: function(e) {
20768
20769         var lel = this.getEl();
20770         var del = this.getDragEl();
20771
20772         // Show the drag frame briefly so we can get its position
20773         del.style.visibility = "";
20774
20775         this.beforeMove();
20776         // Hide the linked element before the move to get around a Safari
20777         // rendering bug.
20778         lel.style.visibility = "hidden";
20779         Roo.dd.DDM.moveToEl(lel, del);
20780         del.style.visibility = "hidden";
20781         lel.style.visibility = "";
20782
20783         this.afterDrag();
20784     },
20785
20786     beforeMove : function(){
20787
20788     },
20789
20790     afterDrag : function(){
20791
20792     },
20793
20794     toString: function() {
20795         return ("DDProxy " + this.id);
20796     }
20797
20798 });
20799 /*
20800  * Based on:
20801  * Ext JS Library 1.1.1
20802  * Copyright(c) 2006-2007, Ext JS, LLC.
20803  *
20804  * Originally Released Under LGPL - original licence link has changed is not relivant.
20805  *
20806  * Fork - LGPL
20807  * <script type="text/javascript">
20808  */
20809
20810  /**
20811  * @class Roo.dd.DDTarget
20812  * A DragDrop implementation that does not move, but can be a drop
20813  * target.  You would get the same result by simply omitting implementation
20814  * for the event callbacks, but this way we reduce the processing cost of the
20815  * event listener and the callbacks.
20816  * @extends Roo.dd.DragDrop
20817  * @constructor
20818  * @param {String} id the id of the element that is a drop target
20819  * @param {String} sGroup the group of related DragDrop objects
20820  * @param {object} config an object containing configurable attributes
20821  *                 Valid properties for DDTarget in addition to those in
20822  *                 DragDrop:
20823  *                    none
20824  */
20825 Roo.dd.DDTarget = function(id, sGroup, config) {
20826     if (id) {
20827         this.initTarget(id, sGroup, config);
20828     }
20829     if (config.listeners || config.events) { 
20830        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20831             listeners : config.listeners || {}, 
20832             events : config.events || {} 
20833         });    
20834     }
20835 };
20836
20837 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20838 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20839     toString: function() {
20840         return ("DDTarget " + this.id);
20841     }
20842 });
20843 /*
20844  * Based on:
20845  * Ext JS Library 1.1.1
20846  * Copyright(c) 2006-2007, Ext JS, LLC.
20847  *
20848  * Originally Released Under LGPL - original licence link has changed is not relivant.
20849  *
20850  * Fork - LGPL
20851  * <script type="text/javascript">
20852  */
20853  
20854
20855 /**
20856  * @class Roo.dd.ScrollManager
20857  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20858  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20859  * @singleton
20860  */
20861 Roo.dd.ScrollManager = function(){
20862     var ddm = Roo.dd.DragDropMgr;
20863     var els = {};
20864     var dragEl = null;
20865     var proc = {};
20866     
20867     
20868     
20869     var onStop = function(e){
20870         dragEl = null;
20871         clearProc();
20872     };
20873     
20874     var triggerRefresh = function(){
20875         if(ddm.dragCurrent){
20876              ddm.refreshCache(ddm.dragCurrent.groups);
20877         }
20878     };
20879     
20880     var doScroll = function(){
20881         if(ddm.dragCurrent){
20882             var dds = Roo.dd.ScrollManager;
20883             if(!dds.animate){
20884                 if(proc.el.scroll(proc.dir, dds.increment)){
20885                     triggerRefresh();
20886                 }
20887             }else{
20888                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
20889             }
20890         }
20891     };
20892     
20893     var clearProc = function(){
20894         if(proc.id){
20895             clearInterval(proc.id);
20896         }
20897         proc.id = 0;
20898         proc.el = null;
20899         proc.dir = "";
20900     };
20901     
20902     var startProc = function(el, dir){
20903          Roo.log('scroll startproc');
20904         clearProc();
20905         proc.el = el;
20906         proc.dir = dir;
20907         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
20908     };
20909     
20910     var onFire = function(e, isDrop){
20911        
20912         if(isDrop || !ddm.dragCurrent){ return; }
20913         var dds = Roo.dd.ScrollManager;
20914         if(!dragEl || dragEl != ddm.dragCurrent){
20915             dragEl = ddm.dragCurrent;
20916             // refresh regions on drag start
20917             dds.refreshCache();
20918         }
20919         
20920         var xy = Roo.lib.Event.getXY(e);
20921         var pt = new Roo.lib.Point(xy[0], xy[1]);
20922         for(var id in els){
20923             var el = els[id], r = el._region;
20924             if(r && r.contains(pt) && el.isScrollable()){
20925                 if(r.bottom - pt.y <= dds.thresh){
20926                     if(proc.el != el){
20927                         startProc(el, "down");
20928                     }
20929                     return;
20930                 }else if(r.right - pt.x <= dds.thresh){
20931                     if(proc.el != el){
20932                         startProc(el, "left");
20933                     }
20934                     return;
20935                 }else if(pt.y - r.top <= dds.thresh){
20936                     if(proc.el != el){
20937                         startProc(el, "up");
20938                     }
20939                     return;
20940                 }else if(pt.x - r.left <= dds.thresh){
20941                     if(proc.el != el){
20942                         startProc(el, "right");
20943                     }
20944                     return;
20945                 }
20946             }
20947         }
20948         clearProc();
20949     };
20950     
20951     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
20952     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
20953     
20954     return {
20955         /**
20956          * Registers new overflow element(s) to auto scroll
20957          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
20958          */
20959         register : function(el){
20960             if(el instanceof Array){
20961                 for(var i = 0, len = el.length; i < len; i++) {
20962                         this.register(el[i]);
20963                 }
20964             }else{
20965                 el = Roo.get(el);
20966                 els[el.id] = el;
20967             }
20968             Roo.dd.ScrollManager.els = els;
20969         },
20970         
20971         /**
20972          * Unregisters overflow element(s) so they are no longer scrolled
20973          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
20974          */
20975         unregister : function(el){
20976             if(el instanceof Array){
20977                 for(var i = 0, len = el.length; i < len; i++) {
20978                         this.unregister(el[i]);
20979                 }
20980             }else{
20981                 el = Roo.get(el);
20982                 delete els[el.id];
20983             }
20984         },
20985         
20986         /**
20987          * The number of pixels from the edge of a container the pointer needs to be to 
20988          * trigger scrolling (defaults to 25)
20989          * @type Number
20990          */
20991         thresh : 25,
20992         
20993         /**
20994          * The number of pixels to scroll in each scroll increment (defaults to 50)
20995          * @type Number
20996          */
20997         increment : 100,
20998         
20999         /**
21000          * The frequency of scrolls in milliseconds (defaults to 500)
21001          * @type Number
21002          */
21003         frequency : 500,
21004         
21005         /**
21006          * True to animate the scroll (defaults to true)
21007          * @type Boolean
21008          */
21009         animate: true,
21010         
21011         /**
21012          * The animation duration in seconds - 
21013          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21014          * @type Number
21015          */
21016         animDuration: .4,
21017         
21018         /**
21019          * Manually trigger a cache refresh.
21020          */
21021         refreshCache : function(){
21022             for(var id in els){
21023                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21024                     els[id]._region = els[id].getRegion();
21025                 }
21026             }
21027         }
21028     };
21029 }();/*
21030  * Based on:
21031  * Ext JS Library 1.1.1
21032  * Copyright(c) 2006-2007, Ext JS, LLC.
21033  *
21034  * Originally Released Under LGPL - original licence link has changed is not relivant.
21035  *
21036  * Fork - LGPL
21037  * <script type="text/javascript">
21038  */
21039  
21040
21041 /**
21042  * @class Roo.dd.Registry
21043  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21044  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21045  * @singleton
21046  */
21047 Roo.dd.Registry = function(){
21048     var elements = {}; 
21049     var handles = {}; 
21050     var autoIdSeed = 0;
21051
21052     var getId = function(el, autogen){
21053         if(typeof el == "string"){
21054             return el;
21055         }
21056         var id = el.id;
21057         if(!id && autogen !== false){
21058             id = "roodd-" + (++autoIdSeed);
21059             el.id = id;
21060         }
21061         return id;
21062     };
21063     
21064     return {
21065     /**
21066      * Register a drag drop element
21067      * @param {String|HTMLElement} element The id or DOM node to register
21068      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21069      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21070      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21071      * populated in the data object (if applicable):
21072      * <pre>
21073 Value      Description<br />
21074 ---------  ------------------------------------------<br />
21075 handles    Array of DOM nodes that trigger dragging<br />
21076            for the element being registered<br />
21077 isHandle   True if the element passed in triggers<br />
21078            dragging itself, else false
21079 </pre>
21080      */
21081         register : function(el, data){
21082             data = data || {};
21083             if(typeof el == "string"){
21084                 el = document.getElementById(el);
21085             }
21086             data.ddel = el;
21087             elements[getId(el)] = data;
21088             if(data.isHandle !== false){
21089                 handles[data.ddel.id] = data;
21090             }
21091             if(data.handles){
21092                 var hs = data.handles;
21093                 for(var i = 0, len = hs.length; i < len; i++){
21094                         handles[getId(hs[i])] = data;
21095                 }
21096             }
21097         },
21098
21099     /**
21100      * Unregister a drag drop element
21101      * @param {String|HTMLElement}  element The id or DOM node to unregister
21102      */
21103         unregister : function(el){
21104             var id = getId(el, false);
21105             var data = elements[id];
21106             if(data){
21107                 delete elements[id];
21108                 if(data.handles){
21109                     var hs = data.handles;
21110                     for(var i = 0, len = hs.length; i < len; i++){
21111                         delete handles[getId(hs[i], false)];
21112                     }
21113                 }
21114             }
21115         },
21116
21117     /**
21118      * Returns the handle registered for a DOM Node by id
21119      * @param {String|HTMLElement} id The DOM node or id to look up
21120      * @return {Object} handle The custom handle data
21121      */
21122         getHandle : function(id){
21123             if(typeof id != "string"){ // must be element?
21124                 id = id.id;
21125             }
21126             return handles[id];
21127         },
21128
21129     /**
21130      * Returns the handle that is registered for the DOM node that is the target of the event
21131      * @param {Event} e The event
21132      * @return {Object} handle The custom handle data
21133      */
21134         getHandleFromEvent : function(e){
21135             var t = Roo.lib.Event.getTarget(e);
21136             return t ? handles[t.id] : null;
21137         },
21138
21139     /**
21140      * Returns a custom data object that is registered for a DOM node by id
21141      * @param {String|HTMLElement} id The DOM node or id to look up
21142      * @return {Object} data The custom data
21143      */
21144         getTarget : function(id){
21145             if(typeof id != "string"){ // must be element?
21146                 id = id.id;
21147             }
21148             return elements[id];
21149         },
21150
21151     /**
21152      * Returns a custom data object that is registered for the DOM node that is the target of the event
21153      * @param {Event} e The event
21154      * @return {Object} data The custom data
21155      */
21156         getTargetFromEvent : function(e){
21157             var t = Roo.lib.Event.getTarget(e);
21158             return t ? elements[t.id] || handles[t.id] : null;
21159         }
21160     };
21161 }();/*
21162  * Based on:
21163  * Ext JS Library 1.1.1
21164  * Copyright(c) 2006-2007, Ext JS, LLC.
21165  *
21166  * Originally Released Under LGPL - original licence link has changed is not relivant.
21167  *
21168  * Fork - LGPL
21169  * <script type="text/javascript">
21170  */
21171  
21172
21173 /**
21174  * @class Roo.dd.StatusProxy
21175  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21176  * default drag proxy used by all Roo.dd components.
21177  * @constructor
21178  * @param {Object} config
21179  */
21180 Roo.dd.StatusProxy = function(config){
21181     Roo.apply(this, config);
21182     this.id = this.id || Roo.id();
21183     this.el = new Roo.Layer({
21184         dh: {
21185             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21186                 {tag: "div", cls: "x-dd-drop-icon"},
21187                 {tag: "div", cls: "x-dd-drag-ghost"}
21188             ]
21189         }, 
21190         shadow: !config || config.shadow !== false
21191     });
21192     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21193     this.dropStatus = this.dropNotAllowed;
21194 };
21195
21196 Roo.dd.StatusProxy.prototype = {
21197     /**
21198      * @cfg {String} dropAllowed
21199      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21200      */
21201     dropAllowed : "x-dd-drop-ok",
21202     /**
21203      * @cfg {String} dropNotAllowed
21204      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21205      */
21206     dropNotAllowed : "x-dd-drop-nodrop",
21207
21208     /**
21209      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21210      * over the current target element.
21211      * @param {String} cssClass The css class for the new drop status indicator image
21212      */
21213     setStatus : function(cssClass){
21214         cssClass = cssClass || this.dropNotAllowed;
21215         if(this.dropStatus != cssClass){
21216             this.el.replaceClass(this.dropStatus, cssClass);
21217             this.dropStatus = cssClass;
21218         }
21219     },
21220
21221     /**
21222      * Resets the status indicator to the default dropNotAllowed value
21223      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21224      */
21225     reset : function(clearGhost){
21226         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21227         this.dropStatus = this.dropNotAllowed;
21228         if(clearGhost){
21229             this.ghost.update("");
21230         }
21231     },
21232
21233     /**
21234      * Updates the contents of the ghost element
21235      * @param {String} html The html that will replace the current innerHTML of the ghost element
21236      */
21237     update : function(html){
21238         if(typeof html == "string"){
21239             this.ghost.update(html);
21240         }else{
21241             this.ghost.update("");
21242             html.style.margin = "0";
21243             this.ghost.dom.appendChild(html);
21244         }
21245         // ensure float = none set?? cant remember why though.
21246         var el = this.ghost.dom.firstChild;
21247                 if(el){
21248                         Roo.fly(el).setStyle('float', 'none');
21249                 }
21250     },
21251     
21252     /**
21253      * Returns the underlying proxy {@link Roo.Layer}
21254      * @return {Roo.Layer} el
21255     */
21256     getEl : function(){
21257         return this.el;
21258     },
21259
21260     /**
21261      * Returns the ghost element
21262      * @return {Roo.Element} el
21263      */
21264     getGhost : function(){
21265         return this.ghost;
21266     },
21267
21268     /**
21269      * Hides the proxy
21270      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21271      */
21272     hide : function(clear){
21273         this.el.hide();
21274         if(clear){
21275             this.reset(true);
21276         }
21277     },
21278
21279     /**
21280      * Stops the repair animation if it's currently running
21281      */
21282     stop : function(){
21283         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21284             this.anim.stop();
21285         }
21286     },
21287
21288     /**
21289      * Displays this proxy
21290      */
21291     show : function(){
21292         this.el.show();
21293     },
21294
21295     /**
21296      * Force the Layer to sync its shadow and shim positions to the element
21297      */
21298     sync : function(){
21299         this.el.sync();
21300     },
21301
21302     /**
21303      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21304      * invalid drop operation by the item being dragged.
21305      * @param {Array} xy The XY position of the element ([x, y])
21306      * @param {Function} callback The function to call after the repair is complete
21307      * @param {Object} scope The scope in which to execute the callback
21308      */
21309     repair : function(xy, callback, scope){
21310         this.callback = callback;
21311         this.scope = scope;
21312         if(xy && this.animRepair !== false){
21313             this.el.addClass("x-dd-drag-repair");
21314             this.el.hideUnders(true);
21315             this.anim = this.el.shift({
21316                 duration: this.repairDuration || .5,
21317                 easing: 'easeOut',
21318                 xy: xy,
21319                 stopFx: true,
21320                 callback: this.afterRepair,
21321                 scope: this
21322             });
21323         }else{
21324             this.afterRepair();
21325         }
21326     },
21327
21328     // private
21329     afterRepair : function(){
21330         this.hide(true);
21331         if(typeof this.callback == "function"){
21332             this.callback.call(this.scope || this);
21333         }
21334         this.callback = null;
21335         this.scope = null;
21336     }
21337 };/*
21338  * Based on:
21339  * Ext JS Library 1.1.1
21340  * Copyright(c) 2006-2007, Ext JS, LLC.
21341  *
21342  * Originally Released Under LGPL - original licence link has changed is not relivant.
21343  *
21344  * Fork - LGPL
21345  * <script type="text/javascript">
21346  */
21347
21348 /**
21349  * @class Roo.dd.DragSource
21350  * @extends Roo.dd.DDProxy
21351  * A simple class that provides the basic implementation needed to make any element draggable.
21352  * @constructor
21353  * @param {String/HTMLElement/Element} el The container element
21354  * @param {Object} config
21355  */
21356 Roo.dd.DragSource = function(el, config){
21357     this.el = Roo.get(el);
21358     this.dragData = {};
21359     
21360     Roo.apply(this, config);
21361     
21362     if(!this.proxy){
21363         this.proxy = new Roo.dd.StatusProxy();
21364     }
21365
21366     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21367           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21368     
21369     this.dragging = false;
21370 };
21371
21372 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21373     /**
21374      * @cfg {String} dropAllowed
21375      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21376      */
21377     dropAllowed : "x-dd-drop-ok",
21378     /**
21379      * @cfg {String} dropNotAllowed
21380      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21381      */
21382     dropNotAllowed : "x-dd-drop-nodrop",
21383
21384     /**
21385      * Returns the data object associated with this drag source
21386      * @return {Object} data An object containing arbitrary data
21387      */
21388     getDragData : function(e){
21389         return this.dragData;
21390     },
21391
21392     // private
21393     onDragEnter : function(e, id){
21394         var target = Roo.dd.DragDropMgr.getDDById(id);
21395         this.cachedTarget = target;
21396         if(this.beforeDragEnter(target, e, id) !== false){
21397             if(target.isNotifyTarget){
21398                 var status = target.notifyEnter(this, e, this.dragData);
21399                 this.proxy.setStatus(status);
21400             }else{
21401                 this.proxy.setStatus(this.dropAllowed);
21402             }
21403             
21404             if(this.afterDragEnter){
21405                 /**
21406                  * An empty function by default, but provided so that you can perform a custom action
21407                  * when the dragged item enters the drop target by providing an implementation.
21408                  * @param {Roo.dd.DragDrop} target The drop target
21409                  * @param {Event} e The event object
21410                  * @param {String} id The id of the dragged element
21411                  * @method afterDragEnter
21412                  */
21413                 this.afterDragEnter(target, e, id);
21414             }
21415         }
21416     },
21417
21418     /**
21419      * An empty function by default, but provided so that you can perform a custom action
21420      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21421      * @param {Roo.dd.DragDrop} target The drop target
21422      * @param {Event} e The event object
21423      * @param {String} id The id of the dragged element
21424      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21425      */
21426     beforeDragEnter : function(target, e, id){
21427         return true;
21428     },
21429
21430     // private
21431     alignElWithMouse: function() {
21432         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21433         this.proxy.sync();
21434     },
21435
21436     // private
21437     onDragOver : function(e, id){
21438         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21439         if(this.beforeDragOver(target, e, id) !== false){
21440             if(target.isNotifyTarget){
21441                 var status = target.notifyOver(this, e, this.dragData);
21442                 this.proxy.setStatus(status);
21443             }
21444
21445             if(this.afterDragOver){
21446                 /**
21447                  * An empty function by default, but provided so that you can perform a custom action
21448                  * while the dragged item is over the drop target by providing an implementation.
21449                  * @param {Roo.dd.DragDrop} target The drop target
21450                  * @param {Event} e The event object
21451                  * @param {String} id The id of the dragged element
21452                  * @method afterDragOver
21453                  */
21454                 this.afterDragOver(target, e, id);
21455             }
21456         }
21457     },
21458
21459     /**
21460      * An empty function by default, but provided so that you can perform a custom action
21461      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21462      * @param {Roo.dd.DragDrop} target The drop target
21463      * @param {Event} e The event object
21464      * @param {String} id The id of the dragged element
21465      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21466      */
21467     beforeDragOver : function(target, e, id){
21468         return true;
21469     },
21470
21471     // private
21472     onDragOut : function(e, id){
21473         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21474         if(this.beforeDragOut(target, e, id) !== false){
21475             if(target.isNotifyTarget){
21476                 target.notifyOut(this, e, this.dragData);
21477             }
21478             this.proxy.reset();
21479             if(this.afterDragOut){
21480                 /**
21481                  * An empty function by default, but provided so that you can perform a custom action
21482                  * after the dragged item is dragged out of the target without dropping.
21483                  * @param {Roo.dd.DragDrop} target The drop target
21484                  * @param {Event} e The event object
21485                  * @param {String} id The id of the dragged element
21486                  * @method afterDragOut
21487                  */
21488                 this.afterDragOut(target, e, id);
21489             }
21490         }
21491         this.cachedTarget = null;
21492     },
21493
21494     /**
21495      * An empty function by default, but provided so that you can perform a custom action before the dragged
21496      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21497      * @param {Roo.dd.DragDrop} target The drop target
21498      * @param {Event} e The event object
21499      * @param {String} id The id of the dragged element
21500      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21501      */
21502     beforeDragOut : function(target, e, id){
21503         return true;
21504     },
21505     
21506     // private
21507     onDragDrop : function(e, id){
21508         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21509         if(this.beforeDragDrop(target, e, id) !== false){
21510             if(target.isNotifyTarget){
21511                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21512                     this.onValidDrop(target, e, id);
21513                 }else{
21514                     this.onInvalidDrop(target, e, id);
21515                 }
21516             }else{
21517                 this.onValidDrop(target, e, id);
21518             }
21519             
21520             if(this.afterDragDrop){
21521                 /**
21522                  * An empty function by default, but provided so that you can perform a custom action
21523                  * after a valid drag drop has occurred by providing an implementation.
21524                  * @param {Roo.dd.DragDrop} target The drop target
21525                  * @param {Event} e The event object
21526                  * @param {String} id The id of the dropped element
21527                  * @method afterDragDrop
21528                  */
21529                 this.afterDragDrop(target, e, id);
21530             }
21531         }
21532         delete this.cachedTarget;
21533     },
21534
21535     /**
21536      * An empty function by default, but provided so that you can perform a custom action before the dragged
21537      * item is dropped onto the target and optionally cancel the onDragDrop.
21538      * @param {Roo.dd.DragDrop} target The drop target
21539      * @param {Event} e The event object
21540      * @param {String} id The id of the dragged element
21541      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21542      */
21543     beforeDragDrop : function(target, e, id){
21544         return true;
21545     },
21546
21547     // private
21548     onValidDrop : function(target, e, id){
21549         this.hideProxy();
21550         if(this.afterValidDrop){
21551             /**
21552              * An empty function by default, but provided so that you can perform a custom action
21553              * after a valid drop has occurred by providing an implementation.
21554              * @param {Object} target The target DD 
21555              * @param {Event} e The event object
21556              * @param {String} id The id of the dropped element
21557              * @method afterInvalidDrop
21558              */
21559             this.afterValidDrop(target, e, id);
21560         }
21561     },
21562
21563     // private
21564     getRepairXY : function(e, data){
21565         return this.el.getXY();  
21566     },
21567
21568     // private
21569     onInvalidDrop : function(target, e, id){
21570         this.beforeInvalidDrop(target, e, id);
21571         if(this.cachedTarget){
21572             if(this.cachedTarget.isNotifyTarget){
21573                 this.cachedTarget.notifyOut(this, e, this.dragData);
21574             }
21575             this.cacheTarget = null;
21576         }
21577         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21578
21579         if(this.afterInvalidDrop){
21580             /**
21581              * An empty function by default, but provided so that you can perform a custom action
21582              * after an invalid drop has occurred by providing an implementation.
21583              * @param {Event} e The event object
21584              * @param {String} id The id of the dropped element
21585              * @method afterInvalidDrop
21586              */
21587             this.afterInvalidDrop(e, id);
21588         }
21589     },
21590
21591     // private
21592     afterRepair : function(){
21593         if(Roo.enableFx){
21594             this.el.highlight(this.hlColor || "c3daf9");
21595         }
21596         this.dragging = false;
21597     },
21598
21599     /**
21600      * An empty function by default, but provided so that you can perform a custom action after an invalid
21601      * drop has occurred.
21602      * @param {Roo.dd.DragDrop} target The drop target
21603      * @param {Event} e The event object
21604      * @param {String} id The id of the dragged element
21605      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21606      */
21607     beforeInvalidDrop : function(target, e, id){
21608         return true;
21609     },
21610
21611     // private
21612     handleMouseDown : function(e){
21613         if(this.dragging) {
21614             return;
21615         }
21616         var data = this.getDragData(e);
21617         if(data && this.onBeforeDrag(data, e) !== false){
21618             this.dragData = data;
21619             this.proxy.stop();
21620             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21621         } 
21622     },
21623
21624     /**
21625      * An empty function by default, but provided so that you can perform a custom action before the initial
21626      * drag event begins and optionally cancel it.
21627      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21628      * @param {Event} e The event object
21629      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21630      */
21631     onBeforeDrag : function(data, e){
21632         return true;
21633     },
21634
21635     /**
21636      * An empty function by default, but provided so that you can perform a custom action once the initial
21637      * drag event has begun.  The drag cannot be canceled from this function.
21638      * @param {Number} x The x position of the click on the dragged object
21639      * @param {Number} y The y position of the click on the dragged object
21640      */
21641     onStartDrag : Roo.emptyFn,
21642
21643     // private - YUI override
21644     startDrag : function(x, y){
21645         this.proxy.reset();
21646         this.dragging = true;
21647         this.proxy.update("");
21648         this.onInitDrag(x, y);
21649         this.proxy.show();
21650     },
21651
21652     // private
21653     onInitDrag : function(x, y){
21654         var clone = this.el.dom.cloneNode(true);
21655         clone.id = Roo.id(); // prevent duplicate ids
21656         this.proxy.update(clone);
21657         this.onStartDrag(x, y);
21658         return true;
21659     },
21660
21661     /**
21662      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21663      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21664      */
21665     getProxy : function(){
21666         return this.proxy;  
21667     },
21668
21669     /**
21670      * Hides the drag source's {@link Roo.dd.StatusProxy}
21671      */
21672     hideProxy : function(){
21673         this.proxy.hide();  
21674         this.proxy.reset(true);
21675         this.dragging = false;
21676     },
21677
21678     // private
21679     triggerCacheRefresh : function(){
21680         Roo.dd.DDM.refreshCache(this.groups);
21681     },
21682
21683     // private - override to prevent hiding
21684     b4EndDrag: function(e) {
21685     },
21686
21687     // private - override to prevent moving
21688     endDrag : function(e){
21689         this.onEndDrag(this.dragData, e);
21690     },
21691
21692     // private
21693     onEndDrag : function(data, e){
21694     },
21695     
21696     // private - pin to cursor
21697     autoOffset : function(x, y) {
21698         this.setDelta(-12, -20);
21699     }    
21700 });/*
21701  * Based on:
21702  * Ext JS Library 1.1.1
21703  * Copyright(c) 2006-2007, Ext JS, LLC.
21704  *
21705  * Originally Released Under LGPL - original licence link has changed is not relivant.
21706  *
21707  * Fork - LGPL
21708  * <script type="text/javascript">
21709  */
21710
21711
21712 /**
21713  * @class Roo.dd.DropTarget
21714  * @extends Roo.dd.DDTarget
21715  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21716  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21717  * @constructor
21718  * @param {String/HTMLElement/Element} el The container element
21719  * @param {Object} config
21720  */
21721 Roo.dd.DropTarget = function(el, config){
21722     this.el = Roo.get(el);
21723     
21724     var listeners = false; ;
21725     if (config && config.listeners) {
21726         listeners= config.listeners;
21727         delete config.listeners;
21728     }
21729     Roo.apply(this, config);
21730     
21731     if(this.containerScroll){
21732         Roo.dd.ScrollManager.register(this.el);
21733     }
21734     this.addEvents( {
21735          /**
21736          * @scope Roo.dd.DropTarget
21737          */
21738          
21739          /**
21740          * @event enter
21741          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21742          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21743          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21744          * 
21745          * IMPORTANT : it should set this.overClass and this.dropAllowed
21746          * 
21747          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21748          * @param {Event} e The event
21749          * @param {Object} data An object containing arbitrary data supplied by the drag source
21750          */
21751         "enter" : true,
21752         
21753          /**
21754          * @event over
21755          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21756          * This method will be called on every mouse movement while the drag source is over the drop target.
21757          * This default implementation simply returns the dropAllowed config value.
21758          * 
21759          * IMPORTANT : it should set this.dropAllowed
21760          * 
21761          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21762          * @param {Event} e The event
21763          * @param {Object} data An object containing arbitrary data supplied by the drag source
21764          
21765          */
21766         "over" : true,
21767         /**
21768          * @event out
21769          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21770          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21771          * overClass (if any) from the drop element.
21772          * 
21773          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21774          * @param {Event} e The event
21775          * @param {Object} data An object containing arbitrary data supplied by the drag source
21776          */
21777          "out" : true,
21778          
21779         /**
21780          * @event drop
21781          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21782          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21783          * implementation that does something to process the drop event and returns true so that the drag source's
21784          * repair action does not run.
21785          * 
21786          * IMPORTANT : it should set this.success
21787          * 
21788          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21789          * @param {Event} e The event
21790          * @param {Object} data An object containing arbitrary data supplied by the drag source
21791         */
21792          "drop" : true
21793     });
21794             
21795      
21796     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21797         this.el.dom, 
21798         this.ddGroup || this.group,
21799         {
21800             isTarget: true,
21801             listeners : listeners || {} 
21802            
21803         
21804         }
21805     );
21806
21807 };
21808
21809 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21810     /**
21811      * @cfg {String} overClass
21812      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21813      */
21814      /**
21815      * @cfg {String} ddGroup
21816      * The drag drop group to handle drop events for
21817      */
21818      
21819     /**
21820      * @cfg {String} dropAllowed
21821      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21822      */
21823     dropAllowed : "x-dd-drop-ok",
21824     /**
21825      * @cfg {String} dropNotAllowed
21826      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21827      */
21828     dropNotAllowed : "x-dd-drop-nodrop",
21829     /**
21830      * @cfg {boolean} success
21831      * set this after drop listener.. 
21832      */
21833     success : false,
21834     /**
21835      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21836      * if the drop point is valid for over/enter..
21837      */
21838     valid : false,
21839     // private
21840     isTarget : true,
21841
21842     // private
21843     isNotifyTarget : true,
21844     
21845     /**
21846      * @hide
21847      */
21848     notifyEnter : function(dd, e, data)
21849     {
21850         this.valid = true;
21851         this.fireEvent('enter', dd, e, data);
21852         if(this.overClass){
21853             this.el.addClass(this.overClass);
21854         }
21855         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21856             this.valid ? this.dropAllowed : this.dropNotAllowed
21857         );
21858     },
21859
21860     /**
21861      * @hide
21862      */
21863     notifyOver : function(dd, e, data)
21864     {
21865         this.valid = true;
21866         this.fireEvent('over', dd, e, data);
21867         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21868             this.valid ? this.dropAllowed : this.dropNotAllowed
21869         );
21870     },
21871
21872     /**
21873      * @hide
21874      */
21875     notifyOut : function(dd, e, data)
21876     {
21877         this.fireEvent('out', dd, e, data);
21878         if(this.overClass){
21879             this.el.removeClass(this.overClass);
21880         }
21881     },
21882
21883     /**
21884      * @hide
21885      */
21886     notifyDrop : function(dd, e, data)
21887     {
21888         this.success = false;
21889         this.fireEvent('drop', dd, e, data);
21890         return this.success;
21891     }
21892 });/*
21893  * Based on:
21894  * Ext JS Library 1.1.1
21895  * Copyright(c) 2006-2007, Ext JS, LLC.
21896  *
21897  * Originally Released Under LGPL - original licence link has changed is not relivant.
21898  *
21899  * Fork - LGPL
21900  * <script type="text/javascript">
21901  */
21902
21903
21904 /**
21905  * @class Roo.dd.DragZone
21906  * @extends Roo.dd.DragSource
21907  * This class provides a container DD instance that proxies for multiple child node sources.<br />
21908  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
21909  * @constructor
21910  * @param {String/HTMLElement/Element} el The container element
21911  * @param {Object} config
21912  */
21913 Roo.dd.DragZone = function(el, config){
21914     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
21915     if(this.containerScroll){
21916         Roo.dd.ScrollManager.register(this.el);
21917     }
21918 };
21919
21920 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
21921     /**
21922      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
21923      * for auto scrolling during drag operations.
21924      */
21925     /**
21926      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
21927      * method after a failed drop (defaults to "c3daf9" - light blue)
21928      */
21929
21930     /**
21931      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
21932      * for a valid target to drag based on the mouse down. Override this method
21933      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
21934      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
21935      * @param {EventObject} e The mouse down event
21936      * @return {Object} The dragData
21937      */
21938     getDragData : function(e){
21939         return Roo.dd.Registry.getHandleFromEvent(e);
21940     },
21941     
21942     /**
21943      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
21944      * this.dragData.ddel
21945      * @param {Number} x The x position of the click on the dragged object
21946      * @param {Number} y The y position of the click on the dragged object
21947      * @return {Boolean} true to continue the drag, false to cancel
21948      */
21949     onInitDrag : function(x, y){
21950         this.proxy.update(this.dragData.ddel.cloneNode(true));
21951         this.onStartDrag(x, y);
21952         return true;
21953     },
21954     
21955     /**
21956      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
21957      */
21958     afterRepair : function(){
21959         if(Roo.enableFx){
21960             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
21961         }
21962         this.dragging = false;
21963     },
21964
21965     /**
21966      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
21967      * the XY of this.dragData.ddel
21968      * @param {EventObject} e The mouse up event
21969      * @return {Array} The xy location (e.g. [100, 200])
21970      */
21971     getRepairXY : function(e){
21972         return Roo.Element.fly(this.dragData.ddel).getXY();  
21973     }
21974 });/*
21975  * Based on:
21976  * Ext JS Library 1.1.1
21977  * Copyright(c) 2006-2007, Ext JS, LLC.
21978  *
21979  * Originally Released Under LGPL - original licence link has changed is not relivant.
21980  *
21981  * Fork - LGPL
21982  * <script type="text/javascript">
21983  */
21984 /**
21985  * @class Roo.dd.DropZone
21986  * @extends Roo.dd.DropTarget
21987  * This class provides a container DD instance that proxies for multiple child node targets.<br />
21988  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
21989  * @constructor
21990  * @param {String/HTMLElement/Element} el The container element
21991  * @param {Object} config
21992  */
21993 Roo.dd.DropZone = function(el, config){
21994     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
21995 };
21996
21997 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
21998     /**
21999      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22000      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22001      * provide your own custom lookup.
22002      * @param {Event} e The event
22003      * @return {Object} data The custom data
22004      */
22005     getTargetFromEvent : function(e){
22006         return Roo.dd.Registry.getTargetFromEvent(e);
22007     },
22008
22009     /**
22010      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22011      * that it has registered.  This method has no default implementation and should be overridden to provide
22012      * node-specific processing if necessary.
22013      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22014      * {@link #getTargetFromEvent} for this node)
22015      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22016      * @param {Event} e The event
22017      * @param {Object} data An object containing arbitrary data supplied by the drag source
22018      */
22019     onNodeEnter : function(n, dd, e, data){
22020         
22021     },
22022
22023     /**
22024      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22025      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22026      * overridden to provide the proper feedback.
22027      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22028      * {@link #getTargetFromEvent} for this node)
22029      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22030      * @param {Event} e The event
22031      * @param {Object} data An object containing arbitrary data supplied by the drag source
22032      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22033      * underlying {@link Roo.dd.StatusProxy} can be updated
22034      */
22035     onNodeOver : function(n, dd, e, data){
22036         return this.dropAllowed;
22037     },
22038
22039     /**
22040      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22041      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22042      * node-specific processing if necessary.
22043      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22044      * {@link #getTargetFromEvent} for this node)
22045      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22046      * @param {Event} e The event
22047      * @param {Object} data An object containing arbitrary data supplied by the drag source
22048      */
22049     onNodeOut : function(n, dd, e, data){
22050         
22051     },
22052
22053     /**
22054      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22055      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22056      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22057      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22058      * {@link #getTargetFromEvent} for this node)
22059      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22060      * @param {Event} e The event
22061      * @param {Object} data An object containing arbitrary data supplied by the drag source
22062      * @return {Boolean} True if the drop was valid, else false
22063      */
22064     onNodeDrop : function(n, dd, e, data){
22065         return false;
22066     },
22067
22068     /**
22069      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22070      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22071      * it should be overridden to provide the proper feedback if necessary.
22072      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22073      * @param {Event} e The event
22074      * @param {Object} data An object containing arbitrary data supplied by the drag source
22075      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22076      * underlying {@link Roo.dd.StatusProxy} can be updated
22077      */
22078     onContainerOver : function(dd, e, data){
22079         return this.dropNotAllowed;
22080     },
22081
22082     /**
22083      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22084      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22085      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22086      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22087      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22088      * @param {Event} e The event
22089      * @param {Object} data An object containing arbitrary data supplied by the drag source
22090      * @return {Boolean} True if the drop was valid, else false
22091      */
22092     onContainerDrop : function(dd, e, data){
22093         return false;
22094     },
22095
22096     /**
22097      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22098      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22099      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22100      * you should override this method and provide a custom implementation.
22101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22102      * @param {Event} e The event
22103      * @param {Object} data An object containing arbitrary data supplied by the drag source
22104      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22105      * underlying {@link Roo.dd.StatusProxy} can be updated
22106      */
22107     notifyEnter : function(dd, e, data){
22108         return this.dropNotAllowed;
22109     },
22110
22111     /**
22112      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22113      * This method will be called on every mouse movement while the drag source is over the drop zone.
22114      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22115      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22116      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22117      * registered node, it will call {@link #onContainerOver}.
22118      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22119      * @param {Event} e The event
22120      * @param {Object} data An object containing arbitrary data supplied by the drag source
22121      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22122      * underlying {@link Roo.dd.StatusProxy} can be updated
22123      */
22124     notifyOver : function(dd, e, data){
22125         var n = this.getTargetFromEvent(e);
22126         if(!n){ // not over valid drop target
22127             if(this.lastOverNode){
22128                 this.onNodeOut(this.lastOverNode, dd, e, data);
22129                 this.lastOverNode = null;
22130             }
22131             return this.onContainerOver(dd, e, data);
22132         }
22133         if(this.lastOverNode != n){
22134             if(this.lastOverNode){
22135                 this.onNodeOut(this.lastOverNode, dd, e, data);
22136             }
22137             this.onNodeEnter(n, dd, e, data);
22138             this.lastOverNode = n;
22139         }
22140         return this.onNodeOver(n, dd, e, data);
22141     },
22142
22143     /**
22144      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22145      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22146      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22147      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22148      * @param {Event} e The event
22149      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22150      */
22151     notifyOut : function(dd, e, data){
22152         if(this.lastOverNode){
22153             this.onNodeOut(this.lastOverNode, dd, e, data);
22154             this.lastOverNode = null;
22155         }
22156     },
22157
22158     /**
22159      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22160      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22161      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22162      * otherwise it will call {@link #onContainerDrop}.
22163      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22164      * @param {Event} e The event
22165      * @param {Object} data An object containing arbitrary data supplied by the drag source
22166      * @return {Boolean} True if the drop was valid, else false
22167      */
22168     notifyDrop : function(dd, e, data){
22169         if(this.lastOverNode){
22170             this.onNodeOut(this.lastOverNode, dd, e, data);
22171             this.lastOverNode = null;
22172         }
22173         var n = this.getTargetFromEvent(e);
22174         return n ?
22175             this.onNodeDrop(n, dd, e, data) :
22176             this.onContainerDrop(dd, e, data);
22177     },
22178
22179     // private
22180     triggerCacheRefresh : function(){
22181         Roo.dd.DDM.refreshCache(this.groups);
22182     }  
22183 });/*
22184  * Based on:
22185  * Ext JS Library 1.1.1
22186  * Copyright(c) 2006-2007, Ext JS, LLC.
22187  *
22188  * Originally Released Under LGPL - original licence link has changed is not relivant.
22189  *
22190  * Fork - LGPL
22191  * <script type="text/javascript">
22192  */
22193
22194
22195 /**
22196  * @class Roo.data.SortTypes
22197  * @singleton
22198  * Defines the default sorting (casting?) comparison functions used when sorting data.
22199  */
22200 Roo.data.SortTypes = {
22201     /**
22202      * Default sort that does nothing
22203      * @param {Mixed} s The value being converted
22204      * @return {Mixed} The comparison value
22205      */
22206     none : function(s){
22207         return s;
22208     },
22209     
22210     /**
22211      * The regular expression used to strip tags
22212      * @type {RegExp}
22213      * @property
22214      */
22215     stripTagsRE : /<\/?[^>]+>/gi,
22216     
22217     /**
22218      * Strips all HTML tags to sort on text only
22219      * @param {Mixed} s The value being converted
22220      * @return {String} The comparison value
22221      */
22222     asText : function(s){
22223         return String(s).replace(this.stripTagsRE, "");
22224     },
22225     
22226     /**
22227      * Strips all HTML tags to sort on text only - Case insensitive
22228      * @param {Mixed} s The value being converted
22229      * @return {String} The comparison value
22230      */
22231     asUCText : function(s){
22232         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22233     },
22234     
22235     /**
22236      * Case insensitive string
22237      * @param {Mixed} s The value being converted
22238      * @return {String} The comparison value
22239      */
22240     asUCString : function(s) {
22241         return String(s).toUpperCase();
22242     },
22243     
22244     /**
22245      * Date sorting
22246      * @param {Mixed} s The value being converted
22247      * @return {Number} The comparison value
22248      */
22249     asDate : function(s) {
22250         if(!s){
22251             return 0;
22252         }
22253         if(s instanceof Date){
22254             return s.getTime();
22255         }
22256         return Date.parse(String(s));
22257     },
22258     
22259     /**
22260      * Float sorting
22261      * @param {Mixed} s The value being converted
22262      * @return {Float} The comparison value
22263      */
22264     asFloat : function(s) {
22265         var val = parseFloat(String(s).replace(/,/g, ""));
22266         if(isNaN(val)) {
22267             val = 0;
22268         }
22269         return val;
22270     },
22271     
22272     /**
22273      * Integer sorting
22274      * @param {Mixed} s The value being converted
22275      * @return {Number} The comparison value
22276      */
22277     asInt : function(s) {
22278         var val = parseInt(String(s).replace(/,/g, ""));
22279         if(isNaN(val)) {
22280             val = 0;
22281         }
22282         return val;
22283     }
22284 };/*
22285  * Based on:
22286  * Ext JS Library 1.1.1
22287  * Copyright(c) 2006-2007, Ext JS, LLC.
22288  *
22289  * Originally Released Under LGPL - original licence link has changed is not relivant.
22290  *
22291  * Fork - LGPL
22292  * <script type="text/javascript">
22293  */
22294
22295 /**
22296 * @class Roo.data.Record
22297  * Instances of this class encapsulate both record <em>definition</em> information, and record
22298  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22299  * to access Records cached in an {@link Roo.data.Store} object.<br>
22300  * <p>
22301  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22302  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22303  * objects.<br>
22304  * <p>
22305  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22306  * @constructor
22307  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22308  * {@link #create}. The parameters are the same.
22309  * @param {Array} data An associative Array of data values keyed by the field name.
22310  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22311  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22312  * not specified an integer id is generated.
22313  */
22314 Roo.data.Record = function(data, id){
22315     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22316     this.data = data;
22317 };
22318
22319 /**
22320  * Generate a constructor for a specific record layout.
22321  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22322  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22323  * Each field definition object may contain the following properties: <ul>
22324  * <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,
22325  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22326  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22327  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22328  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22329  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22330  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22331  * this may be omitted.</p></li>
22332  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22333  * <ul><li>auto (Default, implies no conversion)</li>
22334  * <li>string</li>
22335  * <li>int</li>
22336  * <li>float</li>
22337  * <li>boolean</li>
22338  * <li>date</li></ul></p></li>
22339  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22340  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22341  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22342  * by the Reader into an object that will be stored in the Record. It is passed the
22343  * following parameters:<ul>
22344  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22345  * </ul></p></li>
22346  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22347  * </ul>
22348  * <br>usage:<br><pre><code>
22349 var TopicRecord = Roo.data.Record.create(
22350     {name: 'title', mapping: 'topic_title'},
22351     {name: 'author', mapping: 'username'},
22352     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22353     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22354     {name: 'lastPoster', mapping: 'user2'},
22355     {name: 'excerpt', mapping: 'post_text'}
22356 );
22357
22358 var myNewRecord = new TopicRecord({
22359     title: 'Do my job please',
22360     author: 'noobie',
22361     totalPosts: 1,
22362     lastPost: new Date(),
22363     lastPoster: 'Animal',
22364     excerpt: 'No way dude!'
22365 });
22366 myStore.add(myNewRecord);
22367 </code></pre>
22368  * @method create
22369  * @static
22370  */
22371 Roo.data.Record.create = function(o){
22372     var f = function(){
22373         f.superclass.constructor.apply(this, arguments);
22374     };
22375     Roo.extend(f, Roo.data.Record);
22376     var p = f.prototype;
22377     p.fields = new Roo.util.MixedCollection(false, function(field){
22378         return field.name;
22379     });
22380     for(var i = 0, len = o.length; i < len; i++){
22381         p.fields.add(new Roo.data.Field(o[i]));
22382     }
22383     f.getField = function(name){
22384         return p.fields.get(name);  
22385     };
22386     return f;
22387 };
22388
22389 Roo.data.Record.AUTO_ID = 1000;
22390 Roo.data.Record.EDIT = 'edit';
22391 Roo.data.Record.REJECT = 'reject';
22392 Roo.data.Record.COMMIT = 'commit';
22393
22394 Roo.data.Record.prototype = {
22395     /**
22396      * Readonly flag - true if this record has been modified.
22397      * @type Boolean
22398      */
22399     dirty : false,
22400     editing : false,
22401     error: null,
22402     modified: null,
22403
22404     // private
22405     join : function(store){
22406         this.store = store;
22407     },
22408
22409     /**
22410      * Set the named field to the specified value.
22411      * @param {String} name The name of the field to set.
22412      * @param {Object} value The value to set the field to.
22413      */
22414     set : function(name, value){
22415         if(this.data[name] == value){
22416             return;
22417         }
22418         this.dirty = true;
22419         if(!this.modified){
22420             this.modified = {};
22421         }
22422         if(typeof this.modified[name] == 'undefined'){
22423             this.modified[name] = this.data[name];
22424         }
22425         this.data[name] = value;
22426         if(!this.editing && this.store){
22427             this.store.afterEdit(this);
22428         }       
22429     },
22430
22431     /**
22432      * Get the value of the named field.
22433      * @param {String} name The name of the field to get the value of.
22434      * @return {Object} The value of the field.
22435      */
22436     get : function(name){
22437         return this.data[name]; 
22438     },
22439
22440     // private
22441     beginEdit : function(){
22442         this.editing = true;
22443         this.modified = {}; 
22444     },
22445
22446     // private
22447     cancelEdit : function(){
22448         this.editing = false;
22449         delete this.modified;
22450     },
22451
22452     // private
22453     endEdit : function(){
22454         this.editing = false;
22455         if(this.dirty && this.store){
22456             this.store.afterEdit(this);
22457         }
22458     },
22459
22460     /**
22461      * Usually called by the {@link Roo.data.Store} which owns the Record.
22462      * Rejects all changes made to the Record since either creation, or the last commit operation.
22463      * Modified fields are reverted to their original values.
22464      * <p>
22465      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22466      * of reject operations.
22467      */
22468     reject : function(){
22469         var m = this.modified;
22470         for(var n in m){
22471             if(typeof m[n] != "function"){
22472                 this.data[n] = m[n];
22473             }
22474         }
22475         this.dirty = false;
22476         delete this.modified;
22477         this.editing = false;
22478         if(this.store){
22479             this.store.afterReject(this);
22480         }
22481     },
22482
22483     /**
22484      * Usually called by the {@link Roo.data.Store} which owns the Record.
22485      * Commits all changes made to the Record since either creation, or the last commit operation.
22486      * <p>
22487      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22488      * of commit operations.
22489      */
22490     commit : function(){
22491         this.dirty = false;
22492         delete this.modified;
22493         this.editing = false;
22494         if(this.store){
22495             this.store.afterCommit(this);
22496         }
22497     },
22498
22499     // private
22500     hasError : function(){
22501         return this.error != null;
22502     },
22503
22504     // private
22505     clearError : function(){
22506         this.error = null;
22507     },
22508
22509     /**
22510      * Creates a copy of this record.
22511      * @param {String} id (optional) A new record id if you don't want to use this record's id
22512      * @return {Record}
22513      */
22514     copy : function(newId) {
22515         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22516     }
22517 };/*
22518  * Based on:
22519  * Ext JS Library 1.1.1
22520  * Copyright(c) 2006-2007, Ext JS, LLC.
22521  *
22522  * Originally Released Under LGPL - original licence link has changed is not relivant.
22523  *
22524  * Fork - LGPL
22525  * <script type="text/javascript">
22526  */
22527
22528
22529
22530 /**
22531  * @class Roo.data.Store
22532  * @extends Roo.util.Observable
22533  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22534  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22535  * <p>
22536  * 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
22537  * has no knowledge of the format of the data returned by the Proxy.<br>
22538  * <p>
22539  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22540  * instances from the data object. These records are cached and made available through accessor functions.
22541  * @constructor
22542  * Creates a new Store.
22543  * @param {Object} config A config object containing the objects needed for the Store to access data,
22544  * and read the data into Records.
22545  */
22546 Roo.data.Store = function(config){
22547     this.data = new Roo.util.MixedCollection(false);
22548     this.data.getKey = function(o){
22549         return o.id;
22550     };
22551     this.baseParams = {};
22552     // private
22553     this.paramNames = {
22554         "start" : "start",
22555         "limit" : "limit",
22556         "sort" : "sort",
22557         "dir" : "dir",
22558         "multisort" : "_multisort"
22559     };
22560
22561     if(config && config.data){
22562         this.inlineData = config.data;
22563         delete config.data;
22564     }
22565
22566     Roo.apply(this, config);
22567     
22568     if(this.reader){ // reader passed
22569         this.reader = Roo.factory(this.reader, Roo.data);
22570         this.reader.xmodule = this.xmodule || false;
22571         if(!this.recordType){
22572             this.recordType = this.reader.recordType;
22573         }
22574         if(this.reader.onMetaChange){
22575             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22576         }
22577     }
22578
22579     if(this.recordType){
22580         this.fields = this.recordType.prototype.fields;
22581     }
22582     this.modified = [];
22583
22584     this.addEvents({
22585         /**
22586          * @event datachanged
22587          * Fires when the data cache has changed, and a widget which is using this Store
22588          * as a Record cache should refresh its view.
22589          * @param {Store} this
22590          */
22591         datachanged : true,
22592         /**
22593          * @event metachange
22594          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22595          * @param {Store} this
22596          * @param {Object} meta The JSON metadata
22597          */
22598         metachange : true,
22599         /**
22600          * @event add
22601          * Fires when Records have been added to the Store
22602          * @param {Store} this
22603          * @param {Roo.data.Record[]} records The array of Records added
22604          * @param {Number} index The index at which the record(s) were added
22605          */
22606         add : true,
22607         /**
22608          * @event remove
22609          * Fires when a Record has been removed from the Store
22610          * @param {Store} this
22611          * @param {Roo.data.Record} record The Record that was removed
22612          * @param {Number} index The index at which the record was removed
22613          */
22614         remove : true,
22615         /**
22616          * @event update
22617          * Fires when a Record has been updated
22618          * @param {Store} this
22619          * @param {Roo.data.Record} record The Record that was updated
22620          * @param {String} operation The update operation being performed.  Value may be one of:
22621          * <pre><code>
22622  Roo.data.Record.EDIT
22623  Roo.data.Record.REJECT
22624  Roo.data.Record.COMMIT
22625          * </code></pre>
22626          */
22627         update : true,
22628         /**
22629          * @event clear
22630          * Fires when the data cache has been cleared.
22631          * @param {Store} this
22632          */
22633         clear : true,
22634         /**
22635          * @event beforeload
22636          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22637          * the load action will be canceled.
22638          * @param {Store} this
22639          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22640          */
22641         beforeload : true,
22642         /**
22643          * @event beforeloadadd
22644          * Fires after a new set of Records has been loaded.
22645          * @param {Store} this
22646          * @param {Roo.data.Record[]} records The Records that were loaded
22647          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22648          */
22649         beforeloadadd : true,
22650         /**
22651          * @event load
22652          * Fires after a new set of Records has been loaded, before they are added to the store.
22653          * @param {Store} this
22654          * @param {Roo.data.Record[]} records The Records that were loaded
22655          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22656          * @params {Object} return from reader
22657          */
22658         load : true,
22659         /**
22660          * @event loadexception
22661          * Fires if an exception occurs in the Proxy during loading.
22662          * Called with the signature of the Proxy's "loadexception" event.
22663          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22664          * 
22665          * @param {Proxy} 
22666          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22667          * @param {Object} load options 
22668          * @param {Object} jsonData from your request (normally this contains the Exception)
22669          */
22670         loadexception : true
22671     });
22672     
22673     if(this.proxy){
22674         this.proxy = Roo.factory(this.proxy, Roo.data);
22675         this.proxy.xmodule = this.xmodule || false;
22676         this.relayEvents(this.proxy,  ["loadexception"]);
22677     }
22678     this.sortToggle = {};
22679     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22680
22681     Roo.data.Store.superclass.constructor.call(this);
22682
22683     if(this.inlineData){
22684         this.loadData(this.inlineData);
22685         delete this.inlineData;
22686     }
22687 };
22688
22689 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22690      /**
22691     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22692     * without a remote query - used by combo/forms at present.
22693     */
22694     
22695     /**
22696     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22697     */
22698     /**
22699     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22700     */
22701     /**
22702     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22703     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22704     */
22705     /**
22706     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22707     * on any HTTP request
22708     */
22709     /**
22710     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22711     */
22712     /**
22713     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22714     */
22715     multiSort: false,
22716     /**
22717     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22718     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22719     */
22720     remoteSort : false,
22721
22722     /**
22723     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22724      * loaded or when a record is removed. (defaults to false).
22725     */
22726     pruneModifiedRecords : false,
22727
22728     // private
22729     lastOptions : null,
22730
22731     /**
22732      * Add Records to the Store and fires the add event.
22733      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22734      */
22735     add : function(records){
22736         records = [].concat(records);
22737         for(var i = 0, len = records.length; i < len; i++){
22738             records[i].join(this);
22739         }
22740         var index = this.data.length;
22741         this.data.addAll(records);
22742         this.fireEvent("add", this, records, index);
22743     },
22744
22745     /**
22746      * Remove a Record from the Store and fires the remove event.
22747      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22748      */
22749     remove : function(record){
22750         var index = this.data.indexOf(record);
22751         this.data.removeAt(index);
22752         if(this.pruneModifiedRecords){
22753             this.modified.remove(record);
22754         }
22755         this.fireEvent("remove", this, record, index);
22756     },
22757
22758     /**
22759      * Remove all Records from the Store and fires the clear event.
22760      */
22761     removeAll : function(){
22762         this.data.clear();
22763         if(this.pruneModifiedRecords){
22764             this.modified = [];
22765         }
22766         this.fireEvent("clear", this);
22767     },
22768
22769     /**
22770      * Inserts Records to the Store at the given index and fires the add event.
22771      * @param {Number} index The start index at which to insert the passed Records.
22772      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22773      */
22774     insert : function(index, records){
22775         records = [].concat(records);
22776         for(var i = 0, len = records.length; i < len; i++){
22777             this.data.insert(index, records[i]);
22778             records[i].join(this);
22779         }
22780         this.fireEvent("add", this, records, index);
22781     },
22782
22783     /**
22784      * Get the index within the cache of the passed Record.
22785      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22786      * @return {Number} The index of the passed Record. Returns -1 if not found.
22787      */
22788     indexOf : function(record){
22789         return this.data.indexOf(record);
22790     },
22791
22792     /**
22793      * Get the index within the cache of the Record with the passed id.
22794      * @param {String} id The id of the Record to find.
22795      * @return {Number} The index of the Record. Returns -1 if not found.
22796      */
22797     indexOfId : function(id){
22798         return this.data.indexOfKey(id);
22799     },
22800
22801     /**
22802      * Get the Record with the specified id.
22803      * @param {String} id The id of the Record to find.
22804      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22805      */
22806     getById : function(id){
22807         return this.data.key(id);
22808     },
22809
22810     /**
22811      * Get the Record at the specified index.
22812      * @param {Number} index The index of the Record to find.
22813      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22814      */
22815     getAt : function(index){
22816         return this.data.itemAt(index);
22817     },
22818
22819     /**
22820      * Returns a range of Records between specified indices.
22821      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22822      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22823      * @return {Roo.data.Record[]} An array of Records
22824      */
22825     getRange : function(start, end){
22826         return this.data.getRange(start, end);
22827     },
22828
22829     // private
22830     storeOptions : function(o){
22831         o = Roo.apply({}, o);
22832         delete o.callback;
22833         delete o.scope;
22834         this.lastOptions = o;
22835     },
22836
22837     /**
22838      * Loads the Record cache from the configured Proxy using the configured Reader.
22839      * <p>
22840      * If using remote paging, then the first load call must specify the <em>start</em>
22841      * and <em>limit</em> properties in the options.params property to establish the initial
22842      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22843      * <p>
22844      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22845      * and this call will return before the new data has been loaded. Perform any post-processing
22846      * in a callback function, or in a "load" event handler.</strong>
22847      * <p>
22848      * @param {Object} options An object containing properties which control loading options:<ul>
22849      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22850      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22851      * passed the following arguments:<ul>
22852      * <li>r : Roo.data.Record[]</li>
22853      * <li>options: Options object from the load call</li>
22854      * <li>success: Boolean success indicator</li></ul></li>
22855      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22856      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22857      * </ul>
22858      */
22859     load : function(options){
22860         options = options || {};
22861         if(this.fireEvent("beforeload", this, options) !== false){
22862             this.storeOptions(options);
22863             var p = Roo.apply(options.params || {}, this.baseParams);
22864             // if meta was not loaded from remote source.. try requesting it.
22865             if (!this.reader.metaFromRemote) {
22866                 p._requestMeta = 1;
22867             }
22868             if(this.sortInfo && this.remoteSort){
22869                 var pn = this.paramNames;
22870                 p[pn["sort"]] = this.sortInfo.field;
22871                 p[pn["dir"]] = this.sortInfo.direction;
22872             }
22873             if (this.multiSort) {
22874                 var pn = this.paramNames;
22875                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
22876             }
22877             
22878             this.proxy.load(p, this.reader, this.loadRecords, this, options);
22879         }
22880     },
22881
22882     /**
22883      * Reloads the Record cache from the configured Proxy using the configured Reader and
22884      * the options from the last load operation performed.
22885      * @param {Object} options (optional) An object containing properties which may override the options
22886      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
22887      * the most recently used options are reused).
22888      */
22889     reload : function(options){
22890         this.load(Roo.applyIf(options||{}, this.lastOptions));
22891     },
22892
22893     // private
22894     // Called as a callback by the Reader during a load operation.
22895     loadRecords : function(o, options, success){
22896         if(!o || success === false){
22897             if(success !== false){
22898                 this.fireEvent("load", this, [], options, o);
22899             }
22900             if(options.callback){
22901                 options.callback.call(options.scope || this, [], options, false);
22902             }
22903             return;
22904         }
22905         // if data returned failure - throw an exception.
22906         if (o.success === false) {
22907             // show a message if no listener is registered.
22908             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
22909                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
22910             }
22911             // loadmask wil be hooked into this..
22912             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
22913             return;
22914         }
22915         var r = o.records, t = o.totalRecords || r.length;
22916         
22917         this.fireEvent("beforeloadadd", this, r, options, o);
22918         
22919         if(!options || options.add !== true){
22920             if(this.pruneModifiedRecords){
22921                 this.modified = [];
22922             }
22923             for(var i = 0, len = r.length; i < len; i++){
22924                 r[i].join(this);
22925             }
22926             if(this.snapshot){
22927                 this.data = this.snapshot;
22928                 delete this.snapshot;
22929             }
22930             this.data.clear();
22931             this.data.addAll(r);
22932             this.totalLength = t;
22933             this.applySort();
22934             this.fireEvent("datachanged", this);
22935         }else{
22936             this.totalLength = Math.max(t, this.data.length+r.length);
22937             this.add(r);
22938         }
22939         this.fireEvent("load", this, r, options, o);
22940         if(options.callback){
22941             options.callback.call(options.scope || this, r, options, true);
22942         }
22943     },
22944
22945
22946     /**
22947      * Loads data from a passed data block. A Reader which understands the format of the data
22948      * must have been configured in the constructor.
22949      * @param {Object} data The data block from which to read the Records.  The format of the data expected
22950      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
22951      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
22952      */
22953     loadData : function(o, append){
22954         var r = this.reader.readRecords(o);
22955         this.loadRecords(r, {add: append}, true);
22956     },
22957
22958     /**
22959      * Gets the number of cached records.
22960      * <p>
22961      * <em>If using paging, this may not be the total size of the dataset. If the data object
22962      * used by the Reader contains the dataset size, then the getTotalCount() function returns
22963      * the data set size</em>
22964      */
22965     getCount : function(){
22966         return this.data.length || 0;
22967     },
22968
22969     /**
22970      * Gets the total number of records in the dataset as returned by the server.
22971      * <p>
22972      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
22973      * the dataset size</em>
22974      */
22975     getTotalCount : function(){
22976         return this.totalLength || 0;
22977     },
22978
22979     /**
22980      * Returns the sort state of the Store as an object with two properties:
22981      * <pre><code>
22982  field {String} The name of the field by which the Records are sorted
22983  direction {String} The sort order, "ASC" or "DESC"
22984      * </code></pre>
22985      */
22986     getSortState : function(){
22987         return this.sortInfo;
22988     },
22989
22990     // private
22991     applySort : function(){
22992         if(this.sortInfo && !this.remoteSort){
22993             var s = this.sortInfo, f = s.field;
22994             var st = this.fields.get(f).sortType;
22995             var fn = function(r1, r2){
22996                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
22997                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
22998             };
22999             this.data.sort(s.direction, fn);
23000             if(this.snapshot && this.snapshot != this.data){
23001                 this.snapshot.sort(s.direction, fn);
23002             }
23003         }
23004     },
23005
23006     /**
23007      * Sets the default sort column and order to be used by the next load operation.
23008      * @param {String} fieldName The name of the field to sort by.
23009      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23010      */
23011     setDefaultSort : function(field, dir){
23012         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23013     },
23014
23015     /**
23016      * Sort the Records.
23017      * If remote sorting is used, the sort is performed on the server, and the cache is
23018      * reloaded. If local sorting is used, the cache is sorted internally.
23019      * @param {String} fieldName The name of the field to sort by.
23020      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23021      */
23022     sort : function(fieldName, dir){
23023         var f = this.fields.get(fieldName);
23024         if(!dir){
23025             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23026             
23027             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23028                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23029             }else{
23030                 dir = f.sortDir;
23031             }
23032         }
23033         this.sortToggle[f.name] = dir;
23034         this.sortInfo = {field: f.name, direction: dir};
23035         if(!this.remoteSort){
23036             this.applySort();
23037             this.fireEvent("datachanged", this);
23038         }else{
23039             this.load(this.lastOptions);
23040         }
23041     },
23042
23043     /**
23044      * Calls the specified function for each of the Records in the cache.
23045      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23046      * Returning <em>false</em> aborts and exits the iteration.
23047      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23048      */
23049     each : function(fn, scope){
23050         this.data.each(fn, scope);
23051     },
23052
23053     /**
23054      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23055      * (e.g., during paging).
23056      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23057      */
23058     getModifiedRecords : function(){
23059         return this.modified;
23060     },
23061
23062     // private
23063     createFilterFn : function(property, value, anyMatch){
23064         if(!value.exec){ // not a regex
23065             value = String(value);
23066             if(value.length == 0){
23067                 return false;
23068             }
23069             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23070         }
23071         return function(r){
23072             return value.test(r.data[property]);
23073         };
23074     },
23075
23076     /**
23077      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23078      * @param {String} property A field on your records
23079      * @param {Number} start The record index to start at (defaults to 0)
23080      * @param {Number} end The last record index to include (defaults to length - 1)
23081      * @return {Number} The sum
23082      */
23083     sum : function(property, start, end){
23084         var rs = this.data.items, v = 0;
23085         start = start || 0;
23086         end = (end || end === 0) ? end : rs.length-1;
23087
23088         for(var i = start; i <= end; i++){
23089             v += (rs[i].data[property] || 0);
23090         }
23091         return v;
23092     },
23093
23094     /**
23095      * Filter the records by a specified property.
23096      * @param {String} field A field on your records
23097      * @param {String/RegExp} value Either a string that the field
23098      * should start with or a RegExp to test against the field
23099      * @param {Boolean} anyMatch True to match any part not just the beginning
23100      */
23101     filter : function(property, value, anyMatch){
23102         var fn = this.createFilterFn(property, value, anyMatch);
23103         return fn ? this.filterBy(fn) : this.clearFilter();
23104     },
23105
23106     /**
23107      * Filter by a function. The specified function will be called with each
23108      * record in this data source. If the function returns true the record is included,
23109      * otherwise it is filtered.
23110      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23111      * @param {Object} scope (optional) The scope of the function (defaults to this)
23112      */
23113     filterBy : function(fn, scope){
23114         this.snapshot = this.snapshot || this.data;
23115         this.data = this.queryBy(fn, scope||this);
23116         this.fireEvent("datachanged", this);
23117     },
23118
23119     /**
23120      * Query the records by a specified property.
23121      * @param {String} field A field on your records
23122      * @param {String/RegExp} value Either a string that the field
23123      * should start with or a RegExp to test against the field
23124      * @param {Boolean} anyMatch True to match any part not just the beginning
23125      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23126      */
23127     query : function(property, value, anyMatch){
23128         var fn = this.createFilterFn(property, value, anyMatch);
23129         return fn ? this.queryBy(fn) : this.data.clone();
23130     },
23131
23132     /**
23133      * Query by a function. The specified function will be called with each
23134      * record in this data source. If the function returns true the record is included
23135      * in the results.
23136      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23137      * @param {Object} scope (optional) The scope of the function (defaults to this)
23138       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23139      **/
23140     queryBy : function(fn, scope){
23141         var data = this.snapshot || this.data;
23142         return data.filterBy(fn, scope||this);
23143     },
23144
23145     /**
23146      * Collects unique values for a particular dataIndex from this store.
23147      * @param {String} dataIndex The property to collect
23148      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23149      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23150      * @return {Array} An array of the unique values
23151      **/
23152     collect : function(dataIndex, allowNull, bypassFilter){
23153         var d = (bypassFilter === true && this.snapshot) ?
23154                 this.snapshot.items : this.data.items;
23155         var v, sv, r = [], l = {};
23156         for(var i = 0, len = d.length; i < len; i++){
23157             v = d[i].data[dataIndex];
23158             sv = String(v);
23159             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23160                 l[sv] = true;
23161                 r[r.length] = v;
23162             }
23163         }
23164         return r;
23165     },
23166
23167     /**
23168      * Revert to a view of the Record cache with no filtering applied.
23169      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23170      */
23171     clearFilter : function(suppressEvent){
23172         if(this.snapshot && this.snapshot != this.data){
23173             this.data = this.snapshot;
23174             delete this.snapshot;
23175             if(suppressEvent !== true){
23176                 this.fireEvent("datachanged", this);
23177             }
23178         }
23179     },
23180
23181     // private
23182     afterEdit : function(record){
23183         if(this.modified.indexOf(record) == -1){
23184             this.modified.push(record);
23185         }
23186         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23187     },
23188     
23189     // private
23190     afterReject : function(record){
23191         this.modified.remove(record);
23192         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23193     },
23194
23195     // private
23196     afterCommit : function(record){
23197         this.modified.remove(record);
23198         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23199     },
23200
23201     /**
23202      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23203      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23204      */
23205     commitChanges : function(){
23206         var m = this.modified.slice(0);
23207         this.modified = [];
23208         for(var i = 0, len = m.length; i < len; i++){
23209             m[i].commit();
23210         }
23211     },
23212
23213     /**
23214      * Cancel outstanding changes on all changed records.
23215      */
23216     rejectChanges : function(){
23217         var m = this.modified.slice(0);
23218         this.modified = [];
23219         for(var i = 0, len = m.length; i < len; i++){
23220             m[i].reject();
23221         }
23222     },
23223
23224     onMetaChange : function(meta, rtype, o){
23225         this.recordType = rtype;
23226         this.fields = rtype.prototype.fields;
23227         delete this.snapshot;
23228         this.sortInfo = meta.sortInfo || this.sortInfo;
23229         this.modified = [];
23230         this.fireEvent('metachange', this, this.reader.meta);
23231     },
23232     
23233     moveIndex : function(data, type)
23234     {
23235         var index = this.indexOf(data);
23236         
23237         var newIndex = index + type;
23238         
23239         this.remove(data);
23240         
23241         this.insert(newIndex, data);
23242         
23243     }
23244 });/*
23245  * Based on:
23246  * Ext JS Library 1.1.1
23247  * Copyright(c) 2006-2007, Ext JS, LLC.
23248  *
23249  * Originally Released Under LGPL - original licence link has changed is not relivant.
23250  *
23251  * Fork - LGPL
23252  * <script type="text/javascript">
23253  */
23254
23255 /**
23256  * @class Roo.data.SimpleStore
23257  * @extends Roo.data.Store
23258  * Small helper class to make creating Stores from Array data easier.
23259  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23260  * @cfg {Array} fields An array of field definition objects, or field name strings.
23261  * @cfg {Array} data The multi-dimensional array of data
23262  * @constructor
23263  * @param {Object} config
23264  */
23265 Roo.data.SimpleStore = function(config){
23266     Roo.data.SimpleStore.superclass.constructor.call(this, {
23267         isLocal : true,
23268         reader: new Roo.data.ArrayReader({
23269                 id: config.id
23270             },
23271             Roo.data.Record.create(config.fields)
23272         ),
23273         proxy : new Roo.data.MemoryProxy(config.data)
23274     });
23275     this.load();
23276 };
23277 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23278  * Based on:
23279  * Ext JS Library 1.1.1
23280  * Copyright(c) 2006-2007, Ext JS, LLC.
23281  *
23282  * Originally Released Under LGPL - original licence link has changed is not relivant.
23283  *
23284  * Fork - LGPL
23285  * <script type="text/javascript">
23286  */
23287
23288 /**
23289 /**
23290  * @extends Roo.data.Store
23291  * @class Roo.data.JsonStore
23292  * Small helper class to make creating Stores for JSON data easier. <br/>
23293 <pre><code>
23294 var store = new Roo.data.JsonStore({
23295     url: 'get-images.php',
23296     root: 'images',
23297     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23298 });
23299 </code></pre>
23300  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23301  * JsonReader and HttpProxy (unless inline data is provided).</b>
23302  * @cfg {Array} fields An array of field definition objects, or field name strings.
23303  * @constructor
23304  * @param {Object} config
23305  */
23306 Roo.data.JsonStore = function(c){
23307     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23308         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23309         reader: new Roo.data.JsonReader(c, c.fields)
23310     }));
23311 };
23312 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23313  * Based on:
23314  * Ext JS Library 1.1.1
23315  * Copyright(c) 2006-2007, Ext JS, LLC.
23316  *
23317  * Originally Released Under LGPL - original licence link has changed is not relivant.
23318  *
23319  * Fork - LGPL
23320  * <script type="text/javascript">
23321  */
23322
23323  
23324 Roo.data.Field = function(config){
23325     if(typeof config == "string"){
23326         config = {name: config};
23327     }
23328     Roo.apply(this, config);
23329     
23330     if(!this.type){
23331         this.type = "auto";
23332     }
23333     
23334     var st = Roo.data.SortTypes;
23335     // named sortTypes are supported, here we look them up
23336     if(typeof this.sortType == "string"){
23337         this.sortType = st[this.sortType];
23338     }
23339     
23340     // set default sortType for strings and dates
23341     if(!this.sortType){
23342         switch(this.type){
23343             case "string":
23344                 this.sortType = st.asUCString;
23345                 break;
23346             case "date":
23347                 this.sortType = st.asDate;
23348                 break;
23349             default:
23350                 this.sortType = st.none;
23351         }
23352     }
23353
23354     // define once
23355     var stripRe = /[\$,%]/g;
23356
23357     // prebuilt conversion function for this field, instead of
23358     // switching every time we're reading a value
23359     if(!this.convert){
23360         var cv, dateFormat = this.dateFormat;
23361         switch(this.type){
23362             case "":
23363             case "auto":
23364             case undefined:
23365                 cv = function(v){ return v; };
23366                 break;
23367             case "string":
23368                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23369                 break;
23370             case "int":
23371                 cv = function(v){
23372                     return v !== undefined && v !== null && v !== '' ?
23373                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23374                     };
23375                 break;
23376             case "float":
23377                 cv = function(v){
23378                     return v !== undefined && v !== null && v !== '' ?
23379                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23380                     };
23381                 break;
23382             case "bool":
23383             case "boolean":
23384                 cv = function(v){ return v === true || v === "true" || v == 1; };
23385                 break;
23386             case "date":
23387                 cv = function(v){
23388                     if(!v){
23389                         return '';
23390                     }
23391                     if(v instanceof Date){
23392                         return v;
23393                     }
23394                     if(dateFormat){
23395                         if(dateFormat == "timestamp"){
23396                             return new Date(v*1000);
23397                         }
23398                         return Date.parseDate(v, dateFormat);
23399                     }
23400                     var parsed = Date.parse(v);
23401                     return parsed ? new Date(parsed) : null;
23402                 };
23403              break;
23404             
23405         }
23406         this.convert = cv;
23407     }
23408 };
23409
23410 Roo.data.Field.prototype = {
23411     dateFormat: null,
23412     defaultValue: "",
23413     mapping: null,
23414     sortType : null,
23415     sortDir : "ASC"
23416 };/*
23417  * Based on:
23418  * Ext JS Library 1.1.1
23419  * Copyright(c) 2006-2007, Ext JS, LLC.
23420  *
23421  * Originally Released Under LGPL - original licence link has changed is not relivant.
23422  *
23423  * Fork - LGPL
23424  * <script type="text/javascript">
23425  */
23426  
23427 // Base class for reading structured data from a data source.  This class is intended to be
23428 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23429
23430 /**
23431  * @class Roo.data.DataReader
23432  * Base class for reading structured data from a data source.  This class is intended to be
23433  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23434  */
23435
23436 Roo.data.DataReader = function(meta, recordType){
23437     
23438     this.meta = meta;
23439     
23440     this.recordType = recordType instanceof Array ? 
23441         Roo.data.Record.create(recordType) : recordType;
23442 };
23443
23444 Roo.data.DataReader.prototype = {
23445      /**
23446      * Create an empty record
23447      * @param {Object} data (optional) - overlay some values
23448      * @return {Roo.data.Record} record created.
23449      */
23450     newRow :  function(d) {
23451         var da =  {};
23452         this.recordType.prototype.fields.each(function(c) {
23453             switch( c.type) {
23454                 case 'int' : da[c.name] = 0; break;
23455                 case 'date' : da[c.name] = new Date(); break;
23456                 case 'float' : da[c.name] = 0.0; break;
23457                 case 'boolean' : da[c.name] = false; break;
23458                 default : da[c.name] = ""; break;
23459             }
23460             
23461         });
23462         return new this.recordType(Roo.apply(da, d));
23463     }
23464     
23465 };/*
23466  * Based on:
23467  * Ext JS Library 1.1.1
23468  * Copyright(c) 2006-2007, Ext JS, LLC.
23469  *
23470  * Originally Released Under LGPL - original licence link has changed is not relivant.
23471  *
23472  * Fork - LGPL
23473  * <script type="text/javascript">
23474  */
23475
23476 /**
23477  * @class Roo.data.DataProxy
23478  * @extends Roo.data.Observable
23479  * This class is an abstract base class for implementations which provide retrieval of
23480  * unformatted data objects.<br>
23481  * <p>
23482  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23483  * (of the appropriate type which knows how to parse the data object) to provide a block of
23484  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23485  * <p>
23486  * Custom implementations must implement the load method as described in
23487  * {@link Roo.data.HttpProxy#load}.
23488  */
23489 Roo.data.DataProxy = function(){
23490     this.addEvents({
23491         /**
23492          * @event beforeload
23493          * Fires before a network request is made to retrieve a data object.
23494          * @param {Object} This DataProxy object.
23495          * @param {Object} params The params parameter to the load function.
23496          */
23497         beforeload : true,
23498         /**
23499          * @event load
23500          * Fires before the load method's callback is called.
23501          * @param {Object} This DataProxy object.
23502          * @param {Object} o The data object.
23503          * @param {Object} arg The callback argument object passed to the load function.
23504          */
23505         load : true,
23506         /**
23507          * @event loadexception
23508          * Fires if an Exception occurs during data retrieval.
23509          * @param {Object} This DataProxy object.
23510          * @param {Object} o The data object.
23511          * @param {Object} arg The callback argument object passed to the load function.
23512          * @param {Object} e The Exception.
23513          */
23514         loadexception : true
23515     });
23516     Roo.data.DataProxy.superclass.constructor.call(this);
23517 };
23518
23519 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23520
23521     /**
23522      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23523      */
23524 /*
23525  * Based on:
23526  * Ext JS Library 1.1.1
23527  * Copyright(c) 2006-2007, Ext JS, LLC.
23528  *
23529  * Originally Released Under LGPL - original licence link has changed is not relivant.
23530  *
23531  * Fork - LGPL
23532  * <script type="text/javascript">
23533  */
23534 /**
23535  * @class Roo.data.MemoryProxy
23536  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23537  * to the Reader when its load method is called.
23538  * @constructor
23539  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23540  */
23541 Roo.data.MemoryProxy = function(data){
23542     if (data.data) {
23543         data = data.data;
23544     }
23545     Roo.data.MemoryProxy.superclass.constructor.call(this);
23546     this.data = data;
23547 };
23548
23549 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23550     /**
23551      * Load data from the requested source (in this case an in-memory
23552      * data object passed to the constructor), read the data object into
23553      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23554      * process that block using the passed callback.
23555      * @param {Object} params This parameter is not used by the MemoryProxy class.
23556      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23557      * object into a block of Roo.data.Records.
23558      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23559      * The function must be passed <ul>
23560      * <li>The Record block object</li>
23561      * <li>The "arg" argument from the load function</li>
23562      * <li>A boolean success indicator</li>
23563      * </ul>
23564      * @param {Object} scope The scope in which to call the callback
23565      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23566      */
23567     load : function(params, reader, callback, scope, arg){
23568         params = params || {};
23569         var result;
23570         try {
23571             result = reader.readRecords(this.data);
23572         }catch(e){
23573             this.fireEvent("loadexception", this, arg, null, e);
23574             callback.call(scope, null, arg, false);
23575             return;
23576         }
23577         callback.call(scope, result, arg, true);
23578     },
23579     
23580     // private
23581     update : function(params, records){
23582         
23583     }
23584 });/*
23585  * Based on:
23586  * Ext JS Library 1.1.1
23587  * Copyright(c) 2006-2007, Ext JS, LLC.
23588  *
23589  * Originally Released Under LGPL - original licence link has changed is not relivant.
23590  *
23591  * Fork - LGPL
23592  * <script type="text/javascript">
23593  */
23594 /**
23595  * @class Roo.data.HttpProxy
23596  * @extends Roo.data.DataProxy
23597  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23598  * configured to reference a certain URL.<br><br>
23599  * <p>
23600  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23601  * from which the running page was served.<br><br>
23602  * <p>
23603  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23604  * <p>
23605  * Be aware that to enable the browser to parse an XML document, the server must set
23606  * the Content-Type header in the HTTP response to "text/xml".
23607  * @constructor
23608  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23609  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23610  * will be used to make the request.
23611  */
23612 Roo.data.HttpProxy = function(conn){
23613     Roo.data.HttpProxy.superclass.constructor.call(this);
23614     // is conn a conn config or a real conn?
23615     this.conn = conn;
23616     this.useAjax = !conn || !conn.events;
23617   
23618 };
23619
23620 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23621     // thse are take from connection...
23622     
23623     /**
23624      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23625      */
23626     /**
23627      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23628      * extra parameters to each request made by this object. (defaults to undefined)
23629      */
23630     /**
23631      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23632      *  to each request made by this object. (defaults to undefined)
23633      */
23634     /**
23635      * @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)
23636      */
23637     /**
23638      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23639      */
23640      /**
23641      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23642      * @type Boolean
23643      */
23644   
23645
23646     /**
23647      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23648      * @type Boolean
23649      */
23650     /**
23651      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23652      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23653      * a finer-grained basis than the DataProxy events.
23654      */
23655     getConnection : function(){
23656         return this.useAjax ? Roo.Ajax : this.conn;
23657     },
23658
23659     /**
23660      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23661      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23662      * process that block using the passed callback.
23663      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23664      * for the request to the remote server.
23665      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23666      * object into a block of Roo.data.Records.
23667      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23668      * The function must be passed <ul>
23669      * <li>The Record block object</li>
23670      * <li>The "arg" argument from the load function</li>
23671      * <li>A boolean success indicator</li>
23672      * </ul>
23673      * @param {Object} scope The scope in which to call the callback
23674      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23675      */
23676     load : function(params, reader, callback, scope, arg){
23677         if(this.fireEvent("beforeload", this, params) !== false){
23678             var  o = {
23679                 params : params || {},
23680                 request: {
23681                     callback : callback,
23682                     scope : scope,
23683                     arg : arg
23684                 },
23685                 reader: reader,
23686                 callback : this.loadResponse,
23687                 scope: this
23688             };
23689             if(this.useAjax){
23690                 Roo.applyIf(o, this.conn);
23691                 if(this.activeRequest){
23692                     Roo.Ajax.abort(this.activeRequest);
23693                 }
23694                 this.activeRequest = Roo.Ajax.request(o);
23695             }else{
23696                 this.conn.request(o);
23697             }
23698         }else{
23699             callback.call(scope||this, null, arg, false);
23700         }
23701     },
23702
23703     // private
23704     loadResponse : function(o, success, response){
23705         delete this.activeRequest;
23706         if(!success){
23707             this.fireEvent("loadexception", this, o, response);
23708             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23709             return;
23710         }
23711         var result;
23712         try {
23713             result = o.reader.read(response);
23714         }catch(e){
23715             this.fireEvent("loadexception", this, o, response, e);
23716             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23717             return;
23718         }
23719         
23720         this.fireEvent("load", this, o, o.request.arg);
23721         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23722     },
23723
23724     // private
23725     update : function(dataSet){
23726
23727     },
23728
23729     // private
23730     updateResponse : function(dataSet){
23731
23732     }
23733 });/*
23734  * Based on:
23735  * Ext JS Library 1.1.1
23736  * Copyright(c) 2006-2007, Ext JS, LLC.
23737  *
23738  * Originally Released Under LGPL - original licence link has changed is not relivant.
23739  *
23740  * Fork - LGPL
23741  * <script type="text/javascript">
23742  */
23743
23744 /**
23745  * @class Roo.data.ScriptTagProxy
23746  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23747  * other than the originating domain of the running page.<br><br>
23748  * <p>
23749  * <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
23750  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23751  * <p>
23752  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23753  * source code that is used as the source inside a &lt;script> tag.<br><br>
23754  * <p>
23755  * In order for the browser to process the returned data, the server must wrap the data object
23756  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23757  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23758  * depending on whether the callback name was passed:
23759  * <p>
23760  * <pre><code>
23761 boolean scriptTag = false;
23762 String cb = request.getParameter("callback");
23763 if (cb != null) {
23764     scriptTag = true;
23765     response.setContentType("text/javascript");
23766 } else {
23767     response.setContentType("application/x-json");
23768 }
23769 Writer out = response.getWriter();
23770 if (scriptTag) {
23771     out.write(cb + "(");
23772 }
23773 out.print(dataBlock.toJsonString());
23774 if (scriptTag) {
23775     out.write(");");
23776 }
23777 </pre></code>
23778  *
23779  * @constructor
23780  * @param {Object} config A configuration object.
23781  */
23782 Roo.data.ScriptTagProxy = function(config){
23783     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23784     Roo.apply(this, config);
23785     this.head = document.getElementsByTagName("head")[0];
23786 };
23787
23788 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23789
23790 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23791     /**
23792      * @cfg {String} url The URL from which to request the data object.
23793      */
23794     /**
23795      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23796      */
23797     timeout : 30000,
23798     /**
23799      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23800      * the server the name of the callback function set up by the load call to process the returned data object.
23801      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23802      * javascript output which calls this named function passing the data object as its only parameter.
23803      */
23804     callbackParam : "callback",
23805     /**
23806      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23807      * name to the request.
23808      */
23809     nocache : true,
23810
23811     /**
23812      * Load data from the configured URL, read the data object into
23813      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23814      * process that block using the passed callback.
23815      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23816      * for the request to the remote server.
23817      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23818      * object into a block of Roo.data.Records.
23819      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23820      * The function must be passed <ul>
23821      * <li>The Record block object</li>
23822      * <li>The "arg" argument from the load function</li>
23823      * <li>A boolean success indicator</li>
23824      * </ul>
23825      * @param {Object} scope The scope in which to call the callback
23826      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23827      */
23828     load : function(params, reader, callback, scope, arg){
23829         if(this.fireEvent("beforeload", this, params) !== false){
23830
23831             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23832
23833             var url = this.url;
23834             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23835             if(this.nocache){
23836                 url += "&_dc=" + (new Date().getTime());
23837             }
23838             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23839             var trans = {
23840                 id : transId,
23841                 cb : "stcCallback"+transId,
23842                 scriptId : "stcScript"+transId,
23843                 params : params,
23844                 arg : arg,
23845                 url : url,
23846                 callback : callback,
23847                 scope : scope,
23848                 reader : reader
23849             };
23850             var conn = this;
23851
23852             window[trans.cb] = function(o){
23853                 conn.handleResponse(o, trans);
23854             };
23855
23856             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23857
23858             if(this.autoAbort !== false){
23859                 this.abort();
23860             }
23861
23862             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
23863
23864             var script = document.createElement("script");
23865             script.setAttribute("src", url);
23866             script.setAttribute("type", "text/javascript");
23867             script.setAttribute("id", trans.scriptId);
23868             this.head.appendChild(script);
23869
23870             this.trans = trans;
23871         }else{
23872             callback.call(scope||this, null, arg, false);
23873         }
23874     },
23875
23876     // private
23877     isLoading : function(){
23878         return this.trans ? true : false;
23879     },
23880
23881     /**
23882      * Abort the current server request.
23883      */
23884     abort : function(){
23885         if(this.isLoading()){
23886             this.destroyTrans(this.trans);
23887         }
23888     },
23889
23890     // private
23891     destroyTrans : function(trans, isLoaded){
23892         this.head.removeChild(document.getElementById(trans.scriptId));
23893         clearTimeout(trans.timeoutId);
23894         if(isLoaded){
23895             window[trans.cb] = undefined;
23896             try{
23897                 delete window[trans.cb];
23898             }catch(e){}
23899         }else{
23900             // if hasn't been loaded, wait for load to remove it to prevent script error
23901             window[trans.cb] = function(){
23902                 window[trans.cb] = undefined;
23903                 try{
23904                     delete window[trans.cb];
23905                 }catch(e){}
23906             };
23907         }
23908     },
23909
23910     // private
23911     handleResponse : function(o, trans){
23912         this.trans = false;
23913         this.destroyTrans(trans, true);
23914         var result;
23915         try {
23916             result = trans.reader.readRecords(o);
23917         }catch(e){
23918             this.fireEvent("loadexception", this, o, trans.arg, e);
23919             trans.callback.call(trans.scope||window, null, trans.arg, false);
23920             return;
23921         }
23922         this.fireEvent("load", this, o, trans.arg);
23923         trans.callback.call(trans.scope||window, result, trans.arg, true);
23924     },
23925
23926     // private
23927     handleFailure : function(trans){
23928         this.trans = false;
23929         this.destroyTrans(trans, false);
23930         this.fireEvent("loadexception", this, null, trans.arg);
23931         trans.callback.call(trans.scope||window, null, trans.arg, false);
23932     }
23933 });/*
23934  * Based on:
23935  * Ext JS Library 1.1.1
23936  * Copyright(c) 2006-2007, Ext JS, LLC.
23937  *
23938  * Originally Released Under LGPL - original licence link has changed is not relivant.
23939  *
23940  * Fork - LGPL
23941  * <script type="text/javascript">
23942  */
23943
23944 /**
23945  * @class Roo.data.JsonReader
23946  * @extends Roo.data.DataReader
23947  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
23948  * based on mappings in a provided Roo.data.Record constructor.
23949  * 
23950  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
23951  * in the reply previously. 
23952  * 
23953  * <p>
23954  * Example code:
23955  * <pre><code>
23956 var RecordDef = Roo.data.Record.create([
23957     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
23958     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
23959 ]);
23960 var myReader = new Roo.data.JsonReader({
23961     totalProperty: "results",    // The property which contains the total dataset size (optional)
23962     root: "rows",                // The property which contains an Array of row objects
23963     id: "id"                     // The property within each row object that provides an ID for the record (optional)
23964 }, RecordDef);
23965 </code></pre>
23966  * <p>
23967  * This would consume a JSON file like this:
23968  * <pre><code>
23969 { 'results': 2, 'rows': [
23970     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
23971     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
23972 }
23973 </code></pre>
23974  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
23975  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
23976  * paged from the remote server.
23977  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
23978  * @cfg {String} root name of the property which contains the Array of row objects.
23979  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
23980  * @cfg {Array} fields Array of field definition objects
23981  * @constructor
23982  * Create a new JsonReader
23983  * @param {Object} meta Metadata configuration options
23984  * @param {Object} recordType Either an Array of field definition objects,
23985  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
23986  */
23987 Roo.data.JsonReader = function(meta, recordType){
23988     
23989     meta = meta || {};
23990     // set some defaults:
23991     Roo.applyIf(meta, {
23992         totalProperty: 'total',
23993         successProperty : 'success',
23994         root : 'data',
23995         id : 'id'
23996     });
23997     
23998     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
23999 };
24000 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24001     
24002     /**
24003      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24004      * Used by Store query builder to append _requestMeta to params.
24005      * 
24006      */
24007     metaFromRemote : false,
24008     /**
24009      * This method is only used by a DataProxy which has retrieved data from a remote server.
24010      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24011      * @return {Object} data A data block which is used by an Roo.data.Store object as
24012      * a cache of Roo.data.Records.
24013      */
24014     read : function(response){
24015         var json = response.responseText;
24016        
24017         var o = /* eval:var:o */ eval("("+json+")");
24018         if(!o) {
24019             throw {message: "JsonReader.read: Json object not found"};
24020         }
24021         
24022         if(o.metaData){
24023             
24024             delete this.ef;
24025             this.metaFromRemote = true;
24026             this.meta = o.metaData;
24027             this.recordType = Roo.data.Record.create(o.metaData.fields);
24028             this.onMetaChange(this.meta, this.recordType, o);
24029         }
24030         return this.readRecords(o);
24031     },
24032
24033     // private function a store will implement
24034     onMetaChange : function(meta, recordType, o){
24035
24036     },
24037
24038     /**
24039          * @ignore
24040          */
24041     simpleAccess: function(obj, subsc) {
24042         return obj[subsc];
24043     },
24044
24045         /**
24046          * @ignore
24047          */
24048     getJsonAccessor: function(){
24049         var re = /[\[\.]/;
24050         return function(expr) {
24051             try {
24052                 return(re.test(expr))
24053                     ? new Function("obj", "return obj." + expr)
24054                     : function(obj){
24055                         return obj[expr];
24056                     };
24057             } catch(e){}
24058             return Roo.emptyFn;
24059         };
24060     }(),
24061
24062     /**
24063      * Create a data block containing Roo.data.Records from an XML document.
24064      * @param {Object} o An object which contains an Array of row objects in the property specified
24065      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24066      * which contains the total size of the dataset.
24067      * @return {Object} data A data block which is used by an Roo.data.Store object as
24068      * a cache of Roo.data.Records.
24069      */
24070     readRecords : function(o){
24071         /**
24072          * After any data loads, the raw JSON data is available for further custom processing.
24073          * @type Object
24074          */
24075         this.o = o;
24076         var s = this.meta, Record = this.recordType,
24077             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24078
24079 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24080         if (!this.ef) {
24081             if(s.totalProperty) {
24082                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24083                 }
24084                 if(s.successProperty) {
24085                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24086                 }
24087                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24088                 if (s.id) {
24089                         var g = this.getJsonAccessor(s.id);
24090                         this.getId = function(rec) {
24091                                 var r = g(rec);  
24092                                 return (r === undefined || r === "") ? null : r;
24093                         };
24094                 } else {
24095                         this.getId = function(){return null;};
24096                 }
24097             this.ef = [];
24098             for(var jj = 0; jj < fl; jj++){
24099                 f = fi[jj];
24100                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24101                 this.ef[jj] = this.getJsonAccessor(map);
24102             }
24103         }
24104
24105         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24106         if(s.totalProperty){
24107             var vt = parseInt(this.getTotal(o), 10);
24108             if(!isNaN(vt)){
24109                 totalRecords = vt;
24110             }
24111         }
24112         if(s.successProperty){
24113             var vs = this.getSuccess(o);
24114             if(vs === false || vs === 'false'){
24115                 success = false;
24116             }
24117         }
24118         var records = [];
24119         for(var i = 0; i < c; i++){
24120                 var n = root[i];
24121             var values = {};
24122             var id = this.getId(n);
24123             for(var j = 0; j < fl; j++){
24124                 f = fi[j];
24125             var v = this.ef[j](n);
24126             if (!f.convert) {
24127                 Roo.log('missing convert for ' + f.name);
24128                 Roo.log(f);
24129                 continue;
24130             }
24131             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24132             }
24133             var record = new Record(values, id);
24134             record.json = n;
24135             records[i] = record;
24136         }
24137         return {
24138             raw : o,
24139             success : success,
24140             records : records,
24141             totalRecords : totalRecords
24142         };
24143     }
24144 });/*
24145  * Based on:
24146  * Ext JS Library 1.1.1
24147  * Copyright(c) 2006-2007, Ext JS, LLC.
24148  *
24149  * Originally Released Under LGPL - original licence link has changed is not relivant.
24150  *
24151  * Fork - LGPL
24152  * <script type="text/javascript">
24153  */
24154
24155 /**
24156  * @class Roo.data.XmlReader
24157  * @extends Roo.data.DataReader
24158  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24159  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24160  * <p>
24161  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24162  * header in the HTTP response must be set to "text/xml".</em>
24163  * <p>
24164  * Example code:
24165  * <pre><code>
24166 var RecordDef = Roo.data.Record.create([
24167    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24168    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24169 ]);
24170 var myReader = new Roo.data.XmlReader({
24171    totalRecords: "results", // The element which contains the total dataset size (optional)
24172    record: "row",           // The repeated element which contains row information
24173    id: "id"                 // The element within the row that provides an ID for the record (optional)
24174 }, RecordDef);
24175 </code></pre>
24176  * <p>
24177  * This would consume an XML file like this:
24178  * <pre><code>
24179 &lt;?xml?>
24180 &lt;dataset>
24181  &lt;results>2&lt;/results>
24182  &lt;row>
24183    &lt;id>1&lt;/id>
24184    &lt;name>Bill&lt;/name>
24185    &lt;occupation>Gardener&lt;/occupation>
24186  &lt;/row>
24187  &lt;row>
24188    &lt;id>2&lt;/id>
24189    &lt;name>Ben&lt;/name>
24190    &lt;occupation>Horticulturalist&lt;/occupation>
24191  &lt;/row>
24192 &lt;/dataset>
24193 </code></pre>
24194  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24195  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24196  * paged from the remote server.
24197  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24198  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24199  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24200  * a record identifier value.
24201  * @constructor
24202  * Create a new XmlReader
24203  * @param {Object} meta Metadata configuration options
24204  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24205  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24206  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24207  */
24208 Roo.data.XmlReader = function(meta, recordType){
24209     meta = meta || {};
24210     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24211 };
24212 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24213     /**
24214      * This method is only used by a DataProxy which has retrieved data from a remote server.
24215          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24216          * to contain a method called 'responseXML' that returns an XML document object.
24217      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24218      * a cache of Roo.data.Records.
24219      */
24220     read : function(response){
24221         var doc = response.responseXML;
24222         if(!doc) {
24223             throw {message: "XmlReader.read: XML Document not available"};
24224         }
24225         return this.readRecords(doc);
24226     },
24227
24228     /**
24229      * Create a data block containing Roo.data.Records from an XML document.
24230          * @param {Object} doc A parsed XML document.
24231      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24232      * a cache of Roo.data.Records.
24233      */
24234     readRecords : function(doc){
24235         /**
24236          * After any data loads/reads, the raw XML Document is available for further custom processing.
24237          * @type XMLDocument
24238          */
24239         this.xmlData = doc;
24240         var root = doc.documentElement || doc;
24241         var q = Roo.DomQuery;
24242         var recordType = this.recordType, fields = recordType.prototype.fields;
24243         var sid = this.meta.id;
24244         var totalRecords = 0, success = true;
24245         if(this.meta.totalRecords){
24246             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24247         }
24248         
24249         if(this.meta.success){
24250             var sv = q.selectValue(this.meta.success, root, true);
24251             success = sv !== false && sv !== 'false';
24252         }
24253         var records = [];
24254         var ns = q.select(this.meta.record, root);
24255         for(var i = 0, len = ns.length; i < len; i++) {
24256                 var n = ns[i];
24257                 var values = {};
24258                 var id = sid ? q.selectValue(sid, n) : undefined;
24259                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24260                     var f = fields.items[j];
24261                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24262                     v = f.convert(v);
24263                     values[f.name] = v;
24264                 }
24265                 var record = new recordType(values, id);
24266                 record.node = n;
24267                 records[records.length] = record;
24268             }
24269
24270             return {
24271                 success : success,
24272                 records : records,
24273                 totalRecords : totalRecords || records.length
24274             };
24275     }
24276 });/*
24277  * Based on:
24278  * Ext JS Library 1.1.1
24279  * Copyright(c) 2006-2007, Ext JS, LLC.
24280  *
24281  * Originally Released Under LGPL - original licence link has changed is not relivant.
24282  *
24283  * Fork - LGPL
24284  * <script type="text/javascript">
24285  */
24286
24287 /**
24288  * @class Roo.data.ArrayReader
24289  * @extends Roo.data.DataReader
24290  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24291  * Each element of that Array represents a row of data fields. The
24292  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24293  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24294  * <p>
24295  * Example code:.
24296  * <pre><code>
24297 var RecordDef = Roo.data.Record.create([
24298     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24299     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24300 ]);
24301 var myReader = new Roo.data.ArrayReader({
24302     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24303 }, RecordDef);
24304 </code></pre>
24305  * <p>
24306  * This would consume an Array like this:
24307  * <pre><code>
24308 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24309   </code></pre>
24310  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24311  * @constructor
24312  * Create a new JsonReader
24313  * @param {Object} meta Metadata configuration options.
24314  * @param {Object} recordType Either an Array of field definition objects
24315  * as specified to {@link Roo.data.Record#create},
24316  * or an {@link Roo.data.Record} object
24317  * created using {@link Roo.data.Record#create}.
24318  */
24319 Roo.data.ArrayReader = function(meta, recordType){
24320     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24321 };
24322
24323 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24324     /**
24325      * Create a data block containing Roo.data.Records from an XML document.
24326      * @param {Object} o An Array of row objects which represents the dataset.
24327      * @return {Object} data A data block which is used by an Roo.data.Store object as
24328      * a cache of Roo.data.Records.
24329      */
24330     readRecords : function(o){
24331         var sid = this.meta ? this.meta.id : null;
24332         var recordType = this.recordType, fields = recordType.prototype.fields;
24333         var records = [];
24334         var root = o;
24335             for(var i = 0; i < root.length; i++){
24336                     var n = root[i];
24337                 var values = {};
24338                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24339                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24340                 var f = fields.items[j];
24341                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24342                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24343                 v = f.convert(v);
24344                 values[f.name] = v;
24345             }
24346                 var record = new recordType(values, id);
24347                 record.json = n;
24348                 records[records.length] = record;
24349             }
24350             return {
24351                 records : records,
24352                 totalRecords : records.length
24353             };
24354     }
24355 });/*
24356  * Based on:
24357  * Ext JS Library 1.1.1
24358  * Copyright(c) 2006-2007, Ext JS, LLC.
24359  *
24360  * Originally Released Under LGPL - original licence link has changed is not relivant.
24361  *
24362  * Fork - LGPL
24363  * <script type="text/javascript">
24364  */
24365
24366
24367 /**
24368  * @class Roo.data.Tree
24369  * @extends Roo.util.Observable
24370  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24371  * in the tree have most standard DOM functionality.
24372  * @constructor
24373  * @param {Node} root (optional) The root node
24374  */
24375 Roo.data.Tree = function(root){
24376    this.nodeHash = {};
24377    /**
24378     * The root node for this tree
24379     * @type Node
24380     */
24381    this.root = null;
24382    if(root){
24383        this.setRootNode(root);
24384    }
24385    this.addEvents({
24386        /**
24387         * @event append
24388         * Fires when a new child node is appended to a node in this tree.
24389         * @param {Tree} tree The owner tree
24390         * @param {Node} parent The parent node
24391         * @param {Node} node The newly appended node
24392         * @param {Number} index The index of the newly appended node
24393         */
24394        "append" : true,
24395        /**
24396         * @event remove
24397         * Fires when a child node is removed from a node in this tree.
24398         * @param {Tree} tree The owner tree
24399         * @param {Node} parent The parent node
24400         * @param {Node} node The child node removed
24401         */
24402        "remove" : true,
24403        /**
24404         * @event move
24405         * Fires when a node is moved to a new location in the tree
24406         * @param {Tree} tree The owner tree
24407         * @param {Node} node The node moved
24408         * @param {Node} oldParent The old parent of this node
24409         * @param {Node} newParent The new parent of this node
24410         * @param {Number} index The index it was moved to
24411         */
24412        "move" : true,
24413        /**
24414         * @event insert
24415         * Fires when a new child node is inserted in a node in this tree.
24416         * @param {Tree} tree The owner tree
24417         * @param {Node} parent The parent node
24418         * @param {Node} node The child node inserted
24419         * @param {Node} refNode The child node the node was inserted before
24420         */
24421        "insert" : true,
24422        /**
24423         * @event beforeappend
24424         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24425         * @param {Tree} tree The owner tree
24426         * @param {Node} parent The parent node
24427         * @param {Node} node The child node to be appended
24428         */
24429        "beforeappend" : true,
24430        /**
24431         * @event beforeremove
24432         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24433         * @param {Tree} tree The owner tree
24434         * @param {Node} parent The parent node
24435         * @param {Node} node The child node to be removed
24436         */
24437        "beforeremove" : true,
24438        /**
24439         * @event beforemove
24440         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24441         * @param {Tree} tree The owner tree
24442         * @param {Node} node The node being moved
24443         * @param {Node} oldParent The parent of the node
24444         * @param {Node} newParent The new parent the node is moving to
24445         * @param {Number} index The index it is being moved to
24446         */
24447        "beforemove" : true,
24448        /**
24449         * @event beforeinsert
24450         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24451         * @param {Tree} tree The owner tree
24452         * @param {Node} parent The parent node
24453         * @param {Node} node The child node to be inserted
24454         * @param {Node} refNode The child node the node is being inserted before
24455         */
24456        "beforeinsert" : true
24457    });
24458
24459     Roo.data.Tree.superclass.constructor.call(this);
24460 };
24461
24462 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24463     pathSeparator: "/",
24464
24465     proxyNodeEvent : function(){
24466         return this.fireEvent.apply(this, arguments);
24467     },
24468
24469     /**
24470      * Returns the root node for this tree.
24471      * @return {Node}
24472      */
24473     getRootNode : function(){
24474         return this.root;
24475     },
24476
24477     /**
24478      * Sets the root node for this tree.
24479      * @param {Node} node
24480      * @return {Node}
24481      */
24482     setRootNode : function(node){
24483         this.root = node;
24484         node.ownerTree = this;
24485         node.isRoot = true;
24486         this.registerNode(node);
24487         return node;
24488     },
24489
24490     /**
24491      * Gets a node in this tree by its id.
24492      * @param {String} id
24493      * @return {Node}
24494      */
24495     getNodeById : function(id){
24496         return this.nodeHash[id];
24497     },
24498
24499     registerNode : function(node){
24500         this.nodeHash[node.id] = node;
24501     },
24502
24503     unregisterNode : function(node){
24504         delete this.nodeHash[node.id];
24505     },
24506
24507     toString : function(){
24508         return "[Tree"+(this.id?" "+this.id:"")+"]";
24509     }
24510 });
24511
24512 /**
24513  * @class Roo.data.Node
24514  * @extends Roo.util.Observable
24515  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24516  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24517  * @constructor
24518  * @param {Object} attributes The attributes/config for the node
24519  */
24520 Roo.data.Node = function(attributes){
24521     /**
24522      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24523      * @type {Object}
24524      */
24525     this.attributes = attributes || {};
24526     this.leaf = this.attributes.leaf;
24527     /**
24528      * The node id. @type String
24529      */
24530     this.id = this.attributes.id;
24531     if(!this.id){
24532         this.id = Roo.id(null, "ynode-");
24533         this.attributes.id = this.id;
24534     }
24535      
24536     
24537     /**
24538      * All child nodes of this node. @type Array
24539      */
24540     this.childNodes = [];
24541     if(!this.childNodes.indexOf){ // indexOf is a must
24542         this.childNodes.indexOf = function(o){
24543             for(var i = 0, len = this.length; i < len; i++){
24544                 if(this[i] == o) {
24545                     return i;
24546                 }
24547             }
24548             return -1;
24549         };
24550     }
24551     /**
24552      * The parent node for this node. @type Node
24553      */
24554     this.parentNode = null;
24555     /**
24556      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24557      */
24558     this.firstChild = null;
24559     /**
24560      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24561      */
24562     this.lastChild = null;
24563     /**
24564      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24565      */
24566     this.previousSibling = null;
24567     /**
24568      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24569      */
24570     this.nextSibling = null;
24571
24572     this.addEvents({
24573        /**
24574         * @event append
24575         * Fires when a new child node is appended
24576         * @param {Tree} tree The owner tree
24577         * @param {Node} this This node
24578         * @param {Node} node The newly appended node
24579         * @param {Number} index The index of the newly appended node
24580         */
24581        "append" : true,
24582        /**
24583         * @event remove
24584         * Fires when a child node is removed
24585         * @param {Tree} tree The owner tree
24586         * @param {Node} this This node
24587         * @param {Node} node The removed node
24588         */
24589        "remove" : true,
24590        /**
24591         * @event move
24592         * Fires when this node is moved to a new location in the tree
24593         * @param {Tree} tree The owner tree
24594         * @param {Node} this This node
24595         * @param {Node} oldParent The old parent of this node
24596         * @param {Node} newParent The new parent of this node
24597         * @param {Number} index The index it was moved to
24598         */
24599        "move" : true,
24600        /**
24601         * @event insert
24602         * Fires when a new child node is inserted.
24603         * @param {Tree} tree The owner tree
24604         * @param {Node} this This node
24605         * @param {Node} node The child node inserted
24606         * @param {Node} refNode The child node the node was inserted before
24607         */
24608        "insert" : true,
24609        /**
24610         * @event beforeappend
24611         * Fires before a new child is appended, return false to cancel the append.
24612         * @param {Tree} tree The owner tree
24613         * @param {Node} this This node
24614         * @param {Node} node The child node to be appended
24615         */
24616        "beforeappend" : true,
24617        /**
24618         * @event beforeremove
24619         * Fires before a child is removed, return false to cancel the remove.
24620         * @param {Tree} tree The owner tree
24621         * @param {Node} this This node
24622         * @param {Node} node The child node to be removed
24623         */
24624        "beforeremove" : true,
24625        /**
24626         * @event beforemove
24627         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24628         * @param {Tree} tree The owner tree
24629         * @param {Node} this This node
24630         * @param {Node} oldParent The parent of this node
24631         * @param {Node} newParent The new parent this node is moving to
24632         * @param {Number} index The index it is being moved to
24633         */
24634        "beforemove" : true,
24635        /**
24636         * @event beforeinsert
24637         * Fires before a new child is inserted, return false to cancel the insert.
24638         * @param {Tree} tree The owner tree
24639         * @param {Node} this This node
24640         * @param {Node} node The child node to be inserted
24641         * @param {Node} refNode The child node the node is being inserted before
24642         */
24643        "beforeinsert" : true
24644    });
24645     this.listeners = this.attributes.listeners;
24646     Roo.data.Node.superclass.constructor.call(this);
24647 };
24648
24649 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24650     fireEvent : function(evtName){
24651         // first do standard event for this node
24652         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24653             return false;
24654         }
24655         // then bubble it up to the tree if the event wasn't cancelled
24656         var ot = this.getOwnerTree();
24657         if(ot){
24658             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24659                 return false;
24660             }
24661         }
24662         return true;
24663     },
24664
24665     /**
24666      * Returns true if this node is a leaf
24667      * @return {Boolean}
24668      */
24669     isLeaf : function(){
24670         return this.leaf === true;
24671     },
24672
24673     // private
24674     setFirstChild : function(node){
24675         this.firstChild = node;
24676     },
24677
24678     //private
24679     setLastChild : function(node){
24680         this.lastChild = node;
24681     },
24682
24683
24684     /**
24685      * Returns true if this node is the last child of its parent
24686      * @return {Boolean}
24687      */
24688     isLast : function(){
24689        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24690     },
24691
24692     /**
24693      * Returns true if this node is the first child of its parent
24694      * @return {Boolean}
24695      */
24696     isFirst : function(){
24697        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24698     },
24699
24700     hasChildNodes : function(){
24701         return !this.isLeaf() && this.childNodes.length > 0;
24702     },
24703
24704     /**
24705      * Insert node(s) as the last child node of this node.
24706      * @param {Node/Array} node The node or Array of nodes to append
24707      * @return {Node} The appended node if single append, or null if an array was passed
24708      */
24709     appendChild : function(node){
24710         var multi = false;
24711         if(node instanceof Array){
24712             multi = node;
24713         }else if(arguments.length > 1){
24714             multi = arguments;
24715         }
24716         // if passed an array or multiple args do them one by one
24717         if(multi){
24718             for(var i = 0, len = multi.length; i < len; i++) {
24719                 this.appendChild(multi[i]);
24720             }
24721         }else{
24722             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24723                 return false;
24724             }
24725             var index = this.childNodes.length;
24726             var oldParent = node.parentNode;
24727             // it's a move, make sure we move it cleanly
24728             if(oldParent){
24729                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24730                     return false;
24731                 }
24732                 oldParent.removeChild(node);
24733             }
24734             index = this.childNodes.length;
24735             if(index == 0){
24736                 this.setFirstChild(node);
24737             }
24738             this.childNodes.push(node);
24739             node.parentNode = this;
24740             var ps = this.childNodes[index-1];
24741             if(ps){
24742                 node.previousSibling = ps;
24743                 ps.nextSibling = node;
24744             }else{
24745                 node.previousSibling = null;
24746             }
24747             node.nextSibling = null;
24748             this.setLastChild(node);
24749             node.setOwnerTree(this.getOwnerTree());
24750             this.fireEvent("append", this.ownerTree, this, node, index);
24751             if(oldParent){
24752                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24753             }
24754             return node;
24755         }
24756     },
24757
24758     /**
24759      * Removes a child node from this node.
24760      * @param {Node} node The node to remove
24761      * @return {Node} The removed node
24762      */
24763     removeChild : function(node){
24764         var index = this.childNodes.indexOf(node);
24765         if(index == -1){
24766             return false;
24767         }
24768         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24769             return false;
24770         }
24771
24772         // remove it from childNodes collection
24773         this.childNodes.splice(index, 1);
24774
24775         // update siblings
24776         if(node.previousSibling){
24777             node.previousSibling.nextSibling = node.nextSibling;
24778         }
24779         if(node.nextSibling){
24780             node.nextSibling.previousSibling = node.previousSibling;
24781         }
24782
24783         // update child refs
24784         if(this.firstChild == node){
24785             this.setFirstChild(node.nextSibling);
24786         }
24787         if(this.lastChild == node){
24788             this.setLastChild(node.previousSibling);
24789         }
24790
24791         node.setOwnerTree(null);
24792         // clear any references from the node
24793         node.parentNode = null;
24794         node.previousSibling = null;
24795         node.nextSibling = null;
24796         this.fireEvent("remove", this.ownerTree, this, node);
24797         return node;
24798     },
24799
24800     /**
24801      * Inserts the first node before the second node in this nodes childNodes collection.
24802      * @param {Node} node The node to insert
24803      * @param {Node} refNode The node to insert before (if null the node is appended)
24804      * @return {Node} The inserted node
24805      */
24806     insertBefore : function(node, refNode){
24807         if(!refNode){ // like standard Dom, refNode can be null for append
24808             return this.appendChild(node);
24809         }
24810         // nothing to do
24811         if(node == refNode){
24812             return false;
24813         }
24814
24815         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24816             return false;
24817         }
24818         var index = this.childNodes.indexOf(refNode);
24819         var oldParent = node.parentNode;
24820         var refIndex = index;
24821
24822         // when moving internally, indexes will change after remove
24823         if(oldParent == this && this.childNodes.indexOf(node) < index){
24824             refIndex--;
24825         }
24826
24827         // it's a move, make sure we move it cleanly
24828         if(oldParent){
24829             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24830                 return false;
24831             }
24832             oldParent.removeChild(node);
24833         }
24834         if(refIndex == 0){
24835             this.setFirstChild(node);
24836         }
24837         this.childNodes.splice(refIndex, 0, node);
24838         node.parentNode = this;
24839         var ps = this.childNodes[refIndex-1];
24840         if(ps){
24841             node.previousSibling = ps;
24842             ps.nextSibling = node;
24843         }else{
24844             node.previousSibling = null;
24845         }
24846         node.nextSibling = refNode;
24847         refNode.previousSibling = node;
24848         node.setOwnerTree(this.getOwnerTree());
24849         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24850         if(oldParent){
24851             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24852         }
24853         return node;
24854     },
24855
24856     /**
24857      * Returns the child node at the specified index.
24858      * @param {Number} index
24859      * @return {Node}
24860      */
24861     item : function(index){
24862         return this.childNodes[index];
24863     },
24864
24865     /**
24866      * Replaces one child node in this node with another.
24867      * @param {Node} newChild The replacement node
24868      * @param {Node} oldChild The node to replace
24869      * @return {Node} The replaced node
24870      */
24871     replaceChild : function(newChild, oldChild){
24872         this.insertBefore(newChild, oldChild);
24873         this.removeChild(oldChild);
24874         return oldChild;
24875     },
24876
24877     /**
24878      * Returns the index of a child node
24879      * @param {Node} node
24880      * @return {Number} The index of the node or -1 if it was not found
24881      */
24882     indexOf : function(child){
24883         return this.childNodes.indexOf(child);
24884     },
24885
24886     /**
24887      * Returns the tree this node is in.
24888      * @return {Tree}
24889      */
24890     getOwnerTree : function(){
24891         // if it doesn't have one, look for one
24892         if(!this.ownerTree){
24893             var p = this;
24894             while(p){
24895                 if(p.ownerTree){
24896                     this.ownerTree = p.ownerTree;
24897                     break;
24898                 }
24899                 p = p.parentNode;
24900             }
24901         }
24902         return this.ownerTree;
24903     },
24904
24905     /**
24906      * Returns depth of this node (the root node has a depth of 0)
24907      * @return {Number}
24908      */
24909     getDepth : function(){
24910         var depth = 0;
24911         var p = this;
24912         while(p.parentNode){
24913             ++depth;
24914             p = p.parentNode;
24915         }
24916         return depth;
24917     },
24918
24919     // private
24920     setOwnerTree : function(tree){
24921         // if it's move, we need to update everyone
24922         if(tree != this.ownerTree){
24923             if(this.ownerTree){
24924                 this.ownerTree.unregisterNode(this);
24925             }
24926             this.ownerTree = tree;
24927             var cs = this.childNodes;
24928             for(var i = 0, len = cs.length; i < len; i++) {
24929                 cs[i].setOwnerTree(tree);
24930             }
24931             if(tree){
24932                 tree.registerNode(this);
24933             }
24934         }
24935     },
24936
24937     /**
24938      * Returns the path for this node. The path can be used to expand or select this node programmatically.
24939      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
24940      * @return {String} The path
24941      */
24942     getPath : function(attr){
24943         attr = attr || "id";
24944         var p = this.parentNode;
24945         var b = [this.attributes[attr]];
24946         while(p){
24947             b.unshift(p.attributes[attr]);
24948             p = p.parentNode;
24949         }
24950         var sep = this.getOwnerTree().pathSeparator;
24951         return sep + b.join(sep);
24952     },
24953
24954     /**
24955      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
24956      * function call will be the scope provided or the current node. The arguments to the function
24957      * will be the args provided or the current node. If the function returns false at any point,
24958      * the bubble is stopped.
24959      * @param {Function} fn The function to call
24960      * @param {Object} scope (optional) The scope of the function (defaults to current node)
24961      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
24962      */
24963     bubble : function(fn, scope, args){
24964         var p = this;
24965         while(p){
24966             if(fn.call(scope || p, args || p) === false){
24967                 break;
24968             }
24969             p = p.parentNode;
24970         }
24971     },
24972
24973     /**
24974      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
24975      * function call will be the scope provided or the current node. The arguments to the function
24976      * will be the args provided or the current node. If the function returns false at any point,
24977      * the cascade is stopped on that branch.
24978      * @param {Function} fn The function to call
24979      * @param {Object} scope (optional) The scope of the function (defaults to current node)
24980      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
24981      */
24982     cascade : function(fn, scope, args){
24983         if(fn.call(scope || this, args || this) !== false){
24984             var cs = this.childNodes;
24985             for(var i = 0, len = cs.length; i < len; i++) {
24986                 cs[i].cascade(fn, scope, args);
24987             }
24988         }
24989     },
24990
24991     /**
24992      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
24993      * function call will be the scope provided or the current node. The arguments to the function
24994      * will be the args provided or the current node. If the function returns false at any point,
24995      * the iteration stops.
24996      * @param {Function} fn The function to call
24997      * @param {Object} scope (optional) The scope of the function (defaults to current node)
24998      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
24999      */
25000     eachChild : function(fn, scope, args){
25001         var cs = this.childNodes;
25002         for(var i = 0, len = cs.length; i < len; i++) {
25003                 if(fn.call(scope || this, args || cs[i]) === false){
25004                     break;
25005                 }
25006         }
25007     },
25008
25009     /**
25010      * Finds the first child that has the attribute with the specified value.
25011      * @param {String} attribute The attribute name
25012      * @param {Mixed} value The value to search for
25013      * @return {Node} The found child or null if none was found
25014      */
25015     findChild : function(attribute, value){
25016         var cs = this.childNodes;
25017         for(var i = 0, len = cs.length; i < len; i++) {
25018                 if(cs[i].attributes[attribute] == value){
25019                     return cs[i];
25020                 }
25021         }
25022         return null;
25023     },
25024
25025     /**
25026      * Finds the first child by a custom function. The child matches if the function passed
25027      * returns true.
25028      * @param {Function} fn
25029      * @param {Object} scope (optional)
25030      * @return {Node} The found child or null if none was found
25031      */
25032     findChildBy : function(fn, scope){
25033         var cs = this.childNodes;
25034         for(var i = 0, len = cs.length; i < len; i++) {
25035                 if(fn.call(scope||cs[i], cs[i]) === true){
25036                     return cs[i];
25037                 }
25038         }
25039         return null;
25040     },
25041
25042     /**
25043      * Sorts this nodes children using the supplied sort function
25044      * @param {Function} fn
25045      * @param {Object} scope (optional)
25046      */
25047     sort : function(fn, scope){
25048         var cs = this.childNodes;
25049         var len = cs.length;
25050         if(len > 0){
25051             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25052             cs.sort(sortFn);
25053             for(var i = 0; i < len; i++){
25054                 var n = cs[i];
25055                 n.previousSibling = cs[i-1];
25056                 n.nextSibling = cs[i+1];
25057                 if(i == 0){
25058                     this.setFirstChild(n);
25059                 }
25060                 if(i == len-1){
25061                     this.setLastChild(n);
25062                 }
25063             }
25064         }
25065     },
25066
25067     /**
25068      * Returns true if this node is an ancestor (at any point) of the passed node.
25069      * @param {Node} node
25070      * @return {Boolean}
25071      */
25072     contains : function(node){
25073         return node.isAncestor(this);
25074     },
25075
25076     /**
25077      * Returns true if the passed node is an ancestor (at any point) of this node.
25078      * @param {Node} node
25079      * @return {Boolean}
25080      */
25081     isAncestor : function(node){
25082         var p = this.parentNode;
25083         while(p){
25084             if(p == node){
25085                 return true;
25086             }
25087             p = p.parentNode;
25088         }
25089         return false;
25090     },
25091
25092     toString : function(){
25093         return "[Node"+(this.id?" "+this.id:"")+"]";
25094     }
25095 });/*
25096  * Based on:
25097  * Ext JS Library 1.1.1
25098  * Copyright(c) 2006-2007, Ext JS, LLC.
25099  *
25100  * Originally Released Under LGPL - original licence link has changed is not relivant.
25101  *
25102  * Fork - LGPL
25103  * <script type="text/javascript">
25104  */
25105  (function(){ 
25106 /**
25107  * @class Roo.Layer
25108  * @extends Roo.Element
25109  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25110  * automatic maintaining of shadow/shim positions.
25111  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25112  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25113  * you can pass a string with a CSS class name. False turns off the shadow.
25114  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25115  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25116  * @cfg {String} cls CSS class to add to the element
25117  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25118  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25119  * @constructor
25120  * @param {Object} config An object with config options.
25121  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25122  */
25123
25124 Roo.Layer = function(config, existingEl){
25125     config = config || {};
25126     var dh = Roo.DomHelper;
25127     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25128     if(existingEl){
25129         this.dom = Roo.getDom(existingEl);
25130     }
25131     if(!this.dom){
25132         var o = config.dh || {tag: "div", cls: "x-layer"};
25133         this.dom = dh.append(pel, o);
25134     }
25135     if(config.cls){
25136         this.addClass(config.cls);
25137     }
25138     this.constrain = config.constrain !== false;
25139     this.visibilityMode = Roo.Element.VISIBILITY;
25140     if(config.id){
25141         this.id = this.dom.id = config.id;
25142     }else{
25143         this.id = Roo.id(this.dom);
25144     }
25145     this.zindex = config.zindex || this.getZIndex();
25146     this.position("absolute", this.zindex);
25147     if(config.shadow){
25148         this.shadowOffset = config.shadowOffset || 4;
25149         this.shadow = new Roo.Shadow({
25150             offset : this.shadowOffset,
25151             mode : config.shadow
25152         });
25153     }else{
25154         this.shadowOffset = 0;
25155     }
25156     this.useShim = config.shim !== false && Roo.useShims;
25157     this.useDisplay = config.useDisplay;
25158     this.hide();
25159 };
25160
25161 var supr = Roo.Element.prototype;
25162
25163 // shims are shared among layer to keep from having 100 iframes
25164 var shims = [];
25165
25166 Roo.extend(Roo.Layer, Roo.Element, {
25167
25168     getZIndex : function(){
25169         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25170     },
25171
25172     getShim : function(){
25173         if(!this.useShim){
25174             return null;
25175         }
25176         if(this.shim){
25177             return this.shim;
25178         }
25179         var shim = shims.shift();
25180         if(!shim){
25181             shim = this.createShim();
25182             shim.enableDisplayMode('block');
25183             shim.dom.style.display = 'none';
25184             shim.dom.style.visibility = 'visible';
25185         }
25186         var pn = this.dom.parentNode;
25187         if(shim.dom.parentNode != pn){
25188             pn.insertBefore(shim.dom, this.dom);
25189         }
25190         shim.setStyle('z-index', this.getZIndex()-2);
25191         this.shim = shim;
25192         return shim;
25193     },
25194
25195     hideShim : function(){
25196         if(this.shim){
25197             this.shim.setDisplayed(false);
25198             shims.push(this.shim);
25199             delete this.shim;
25200         }
25201     },
25202
25203     disableShadow : function(){
25204         if(this.shadow){
25205             this.shadowDisabled = true;
25206             this.shadow.hide();
25207             this.lastShadowOffset = this.shadowOffset;
25208             this.shadowOffset = 0;
25209         }
25210     },
25211
25212     enableShadow : function(show){
25213         if(this.shadow){
25214             this.shadowDisabled = false;
25215             this.shadowOffset = this.lastShadowOffset;
25216             delete this.lastShadowOffset;
25217             if(show){
25218                 this.sync(true);
25219             }
25220         }
25221     },
25222
25223     // private
25224     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25225     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25226     sync : function(doShow){
25227         var sw = this.shadow;
25228         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25229             var sh = this.getShim();
25230
25231             var w = this.getWidth(),
25232                 h = this.getHeight();
25233
25234             var l = this.getLeft(true),
25235                 t = this.getTop(true);
25236
25237             if(sw && !this.shadowDisabled){
25238                 if(doShow && !sw.isVisible()){
25239                     sw.show(this);
25240                 }else{
25241                     sw.realign(l, t, w, h);
25242                 }
25243                 if(sh){
25244                     if(doShow){
25245                        sh.show();
25246                     }
25247                     // fit the shim behind the shadow, so it is shimmed too
25248                     var a = sw.adjusts, s = sh.dom.style;
25249                     s.left = (Math.min(l, l+a.l))+"px";
25250                     s.top = (Math.min(t, t+a.t))+"px";
25251                     s.width = (w+a.w)+"px";
25252                     s.height = (h+a.h)+"px";
25253                 }
25254             }else if(sh){
25255                 if(doShow){
25256                    sh.show();
25257                 }
25258                 sh.setSize(w, h);
25259                 sh.setLeftTop(l, t);
25260             }
25261             
25262         }
25263     },
25264
25265     // private
25266     destroy : function(){
25267         this.hideShim();
25268         if(this.shadow){
25269             this.shadow.hide();
25270         }
25271         this.removeAllListeners();
25272         var pn = this.dom.parentNode;
25273         if(pn){
25274             pn.removeChild(this.dom);
25275         }
25276         Roo.Element.uncache(this.id);
25277     },
25278
25279     remove : function(){
25280         this.destroy();
25281     },
25282
25283     // private
25284     beginUpdate : function(){
25285         this.updating = true;
25286     },
25287
25288     // private
25289     endUpdate : function(){
25290         this.updating = false;
25291         this.sync(true);
25292     },
25293
25294     // private
25295     hideUnders : function(negOffset){
25296         if(this.shadow){
25297             this.shadow.hide();
25298         }
25299         this.hideShim();
25300     },
25301
25302     // private
25303     constrainXY : function(){
25304         if(this.constrain){
25305             var vw = Roo.lib.Dom.getViewWidth(),
25306                 vh = Roo.lib.Dom.getViewHeight();
25307             var s = Roo.get(document).getScroll();
25308
25309             var xy = this.getXY();
25310             var x = xy[0], y = xy[1];   
25311             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25312             // only move it if it needs it
25313             var moved = false;
25314             // first validate right/bottom
25315             if((x + w) > vw+s.left){
25316                 x = vw - w - this.shadowOffset;
25317                 moved = true;
25318             }
25319             if((y + h) > vh+s.top){
25320                 y = vh - h - this.shadowOffset;
25321                 moved = true;
25322             }
25323             // then make sure top/left isn't negative
25324             if(x < s.left){
25325                 x = s.left;
25326                 moved = true;
25327             }
25328             if(y < s.top){
25329                 y = s.top;
25330                 moved = true;
25331             }
25332             if(moved){
25333                 if(this.avoidY){
25334                     var ay = this.avoidY;
25335                     if(y <= ay && (y+h) >= ay){
25336                         y = ay-h-5;   
25337                     }
25338                 }
25339                 xy = [x, y];
25340                 this.storeXY(xy);
25341                 supr.setXY.call(this, xy);
25342                 this.sync();
25343             }
25344         }
25345     },
25346
25347     isVisible : function(){
25348         return this.visible;    
25349     },
25350
25351     // private
25352     showAction : function(){
25353         this.visible = true; // track visibility to prevent getStyle calls
25354         if(this.useDisplay === true){
25355             this.setDisplayed("");
25356         }else if(this.lastXY){
25357             supr.setXY.call(this, this.lastXY);
25358         }else if(this.lastLT){
25359             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25360         }
25361     },
25362
25363     // private
25364     hideAction : function(){
25365         this.visible = false;
25366         if(this.useDisplay === true){
25367             this.setDisplayed(false);
25368         }else{
25369             this.setLeftTop(-10000,-10000);
25370         }
25371     },
25372
25373     // overridden Element method
25374     setVisible : function(v, a, d, c, e){
25375         if(v){
25376             this.showAction();
25377         }
25378         if(a && v){
25379             var cb = function(){
25380                 this.sync(true);
25381                 if(c){
25382                     c();
25383                 }
25384             }.createDelegate(this);
25385             supr.setVisible.call(this, true, true, d, cb, e);
25386         }else{
25387             if(!v){
25388                 this.hideUnders(true);
25389             }
25390             var cb = c;
25391             if(a){
25392                 cb = function(){
25393                     this.hideAction();
25394                     if(c){
25395                         c();
25396                     }
25397                 }.createDelegate(this);
25398             }
25399             supr.setVisible.call(this, v, a, d, cb, e);
25400             if(v){
25401                 this.sync(true);
25402             }else if(!a){
25403                 this.hideAction();
25404             }
25405         }
25406     },
25407
25408     storeXY : function(xy){
25409         delete this.lastLT;
25410         this.lastXY = xy;
25411     },
25412
25413     storeLeftTop : function(left, top){
25414         delete this.lastXY;
25415         this.lastLT = [left, top];
25416     },
25417
25418     // private
25419     beforeFx : function(){
25420         this.beforeAction();
25421         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25422     },
25423
25424     // private
25425     afterFx : function(){
25426         Roo.Layer.superclass.afterFx.apply(this, arguments);
25427         this.sync(this.isVisible());
25428     },
25429
25430     // private
25431     beforeAction : function(){
25432         if(!this.updating && this.shadow){
25433             this.shadow.hide();
25434         }
25435     },
25436
25437     // overridden Element method
25438     setLeft : function(left){
25439         this.storeLeftTop(left, this.getTop(true));
25440         supr.setLeft.apply(this, arguments);
25441         this.sync();
25442     },
25443
25444     setTop : function(top){
25445         this.storeLeftTop(this.getLeft(true), top);
25446         supr.setTop.apply(this, arguments);
25447         this.sync();
25448     },
25449
25450     setLeftTop : function(left, top){
25451         this.storeLeftTop(left, top);
25452         supr.setLeftTop.apply(this, arguments);
25453         this.sync();
25454     },
25455
25456     setXY : function(xy, a, d, c, e){
25457         this.fixDisplay();
25458         this.beforeAction();
25459         this.storeXY(xy);
25460         var cb = this.createCB(c);
25461         supr.setXY.call(this, xy, a, d, cb, e);
25462         if(!a){
25463             cb();
25464         }
25465     },
25466
25467     // private
25468     createCB : function(c){
25469         var el = this;
25470         return function(){
25471             el.constrainXY();
25472             el.sync(true);
25473             if(c){
25474                 c();
25475             }
25476         };
25477     },
25478
25479     // overridden Element method
25480     setX : function(x, a, d, c, e){
25481         this.setXY([x, this.getY()], a, d, c, e);
25482     },
25483
25484     // overridden Element method
25485     setY : function(y, a, d, c, e){
25486         this.setXY([this.getX(), y], a, d, c, e);
25487     },
25488
25489     // overridden Element method
25490     setSize : function(w, h, a, d, c, e){
25491         this.beforeAction();
25492         var cb = this.createCB(c);
25493         supr.setSize.call(this, w, h, a, d, cb, e);
25494         if(!a){
25495             cb();
25496         }
25497     },
25498
25499     // overridden Element method
25500     setWidth : function(w, a, d, c, e){
25501         this.beforeAction();
25502         var cb = this.createCB(c);
25503         supr.setWidth.call(this, w, a, d, cb, e);
25504         if(!a){
25505             cb();
25506         }
25507     },
25508
25509     // overridden Element method
25510     setHeight : function(h, a, d, c, e){
25511         this.beforeAction();
25512         var cb = this.createCB(c);
25513         supr.setHeight.call(this, h, a, d, cb, e);
25514         if(!a){
25515             cb();
25516         }
25517     },
25518
25519     // overridden Element method
25520     setBounds : function(x, y, w, h, a, d, c, e){
25521         this.beforeAction();
25522         var cb = this.createCB(c);
25523         if(!a){
25524             this.storeXY([x, y]);
25525             supr.setXY.call(this, [x, y]);
25526             supr.setSize.call(this, w, h, a, d, cb, e);
25527             cb();
25528         }else{
25529             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25530         }
25531         return this;
25532     },
25533     
25534     /**
25535      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25536      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25537      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25538      * @param {Number} zindex The new z-index to set
25539      * @return {this} The Layer
25540      */
25541     setZIndex : function(zindex){
25542         this.zindex = zindex;
25543         this.setStyle("z-index", zindex + 2);
25544         if(this.shadow){
25545             this.shadow.setZIndex(zindex + 1);
25546         }
25547         if(this.shim){
25548             this.shim.setStyle("z-index", zindex);
25549         }
25550     }
25551 });
25552 })();/*
25553  * Based on:
25554  * Ext JS Library 1.1.1
25555  * Copyright(c) 2006-2007, Ext JS, LLC.
25556  *
25557  * Originally Released Under LGPL - original licence link has changed is not relivant.
25558  *
25559  * Fork - LGPL
25560  * <script type="text/javascript">
25561  */
25562
25563
25564 /**
25565  * @class Roo.Shadow
25566  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25567  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25568  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25569  * @constructor
25570  * Create a new Shadow
25571  * @param {Object} config The config object
25572  */
25573 Roo.Shadow = function(config){
25574     Roo.apply(this, config);
25575     if(typeof this.mode != "string"){
25576         this.mode = this.defaultMode;
25577     }
25578     var o = this.offset, a = {h: 0};
25579     var rad = Math.floor(this.offset/2);
25580     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25581         case "drop":
25582             a.w = 0;
25583             a.l = a.t = o;
25584             a.t -= 1;
25585             if(Roo.isIE){
25586                 a.l -= this.offset + rad;
25587                 a.t -= this.offset + rad;
25588                 a.w -= rad;
25589                 a.h -= rad;
25590                 a.t += 1;
25591             }
25592         break;
25593         case "sides":
25594             a.w = (o*2);
25595             a.l = -o;
25596             a.t = o-1;
25597             if(Roo.isIE){
25598                 a.l -= (this.offset - rad);
25599                 a.t -= this.offset + rad;
25600                 a.l += 1;
25601                 a.w -= (this.offset - rad)*2;
25602                 a.w -= rad + 1;
25603                 a.h -= 1;
25604             }
25605         break;
25606         case "frame":
25607             a.w = a.h = (o*2);
25608             a.l = a.t = -o;
25609             a.t += 1;
25610             a.h -= 2;
25611             if(Roo.isIE){
25612                 a.l -= (this.offset - rad);
25613                 a.t -= (this.offset - rad);
25614                 a.l += 1;
25615                 a.w -= (this.offset + rad + 1);
25616                 a.h -= (this.offset + rad);
25617                 a.h += 1;
25618             }
25619         break;
25620     };
25621
25622     this.adjusts = a;
25623 };
25624
25625 Roo.Shadow.prototype = {
25626     /**
25627      * @cfg {String} mode
25628      * The shadow display mode.  Supports the following options:<br />
25629      * sides: Shadow displays on both sides and bottom only<br />
25630      * frame: Shadow displays equally on all four sides<br />
25631      * drop: Traditional bottom-right drop shadow (default)
25632      */
25633     /**
25634      * @cfg {String} offset
25635      * The number of pixels to offset the shadow from the element (defaults to 4)
25636      */
25637     offset: 4,
25638
25639     // private
25640     defaultMode: "drop",
25641
25642     /**
25643      * Displays the shadow under the target element
25644      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25645      */
25646     show : function(target){
25647         target = Roo.get(target);
25648         if(!this.el){
25649             this.el = Roo.Shadow.Pool.pull();
25650             if(this.el.dom.nextSibling != target.dom){
25651                 this.el.insertBefore(target);
25652             }
25653         }
25654         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25655         if(Roo.isIE){
25656             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25657         }
25658         this.realign(
25659             target.getLeft(true),
25660             target.getTop(true),
25661             target.getWidth(),
25662             target.getHeight()
25663         );
25664         this.el.dom.style.display = "block";
25665     },
25666
25667     /**
25668      * Returns true if the shadow is visible, else false
25669      */
25670     isVisible : function(){
25671         return this.el ? true : false;  
25672     },
25673
25674     /**
25675      * Direct alignment when values are already available. Show must be called at least once before
25676      * calling this method to ensure it is initialized.
25677      * @param {Number} left The target element left position
25678      * @param {Number} top The target element top position
25679      * @param {Number} width The target element width
25680      * @param {Number} height The target element height
25681      */
25682     realign : function(l, t, w, h){
25683         if(!this.el){
25684             return;
25685         }
25686         var a = this.adjusts, d = this.el.dom, s = d.style;
25687         var iea = 0;
25688         s.left = (l+a.l)+"px";
25689         s.top = (t+a.t)+"px";
25690         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25691  
25692         if(s.width != sws || s.height != shs){
25693             s.width = sws;
25694             s.height = shs;
25695             if(!Roo.isIE){
25696                 var cn = d.childNodes;
25697                 var sww = Math.max(0, (sw-12))+"px";
25698                 cn[0].childNodes[1].style.width = sww;
25699                 cn[1].childNodes[1].style.width = sww;
25700                 cn[2].childNodes[1].style.width = sww;
25701                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25702             }
25703         }
25704     },
25705
25706     /**
25707      * Hides this shadow
25708      */
25709     hide : function(){
25710         if(this.el){
25711             this.el.dom.style.display = "none";
25712             Roo.Shadow.Pool.push(this.el);
25713             delete this.el;
25714         }
25715     },
25716
25717     /**
25718      * Adjust the z-index of this shadow
25719      * @param {Number} zindex The new z-index
25720      */
25721     setZIndex : function(z){
25722         this.zIndex = z;
25723         if(this.el){
25724             this.el.setStyle("z-index", z);
25725         }
25726     }
25727 };
25728
25729 // Private utility class that manages the internal Shadow cache
25730 Roo.Shadow.Pool = function(){
25731     var p = [];
25732     var markup = Roo.isIE ?
25733                  '<div class="x-ie-shadow"></div>' :
25734                  '<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>';
25735     return {
25736         pull : function(){
25737             var sh = p.shift();
25738             if(!sh){
25739                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25740                 sh.autoBoxAdjust = false;
25741             }
25742             return sh;
25743         },
25744
25745         push : function(sh){
25746             p.push(sh);
25747         }
25748     };
25749 }();/*
25750  * Based on:
25751  * Ext JS Library 1.1.1
25752  * Copyright(c) 2006-2007, Ext JS, LLC.
25753  *
25754  * Originally Released Under LGPL - original licence link has changed is not relivant.
25755  *
25756  * Fork - LGPL
25757  * <script type="text/javascript">
25758  */
25759
25760
25761 /**
25762  * @class Roo.SplitBar
25763  * @extends Roo.util.Observable
25764  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25765  * <br><br>
25766  * Usage:
25767  * <pre><code>
25768 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25769                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25770 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25771 split.minSize = 100;
25772 split.maxSize = 600;
25773 split.animate = true;
25774 split.on('moved', splitterMoved);
25775 </code></pre>
25776  * @constructor
25777  * Create a new SplitBar
25778  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25779  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25780  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25781  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25782                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25783                         position of the SplitBar).
25784  */
25785 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25786     
25787     /** @private */
25788     this.el = Roo.get(dragElement, true);
25789     this.el.dom.unselectable = "on";
25790     /** @private */
25791     this.resizingEl = Roo.get(resizingElement, true);
25792
25793     /**
25794      * @private
25795      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25796      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25797      * @type Number
25798      */
25799     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25800     
25801     /**
25802      * The minimum size of the resizing element. (Defaults to 0)
25803      * @type Number
25804      */
25805     this.minSize = 0;
25806     
25807     /**
25808      * The maximum size of the resizing element. (Defaults to 2000)
25809      * @type Number
25810      */
25811     this.maxSize = 2000;
25812     
25813     /**
25814      * Whether to animate the transition to the new size
25815      * @type Boolean
25816      */
25817     this.animate = false;
25818     
25819     /**
25820      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25821      * @type Boolean
25822      */
25823     this.useShim = false;
25824     
25825     /** @private */
25826     this.shim = null;
25827     
25828     if(!existingProxy){
25829         /** @private */
25830         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25831     }else{
25832         this.proxy = Roo.get(existingProxy).dom;
25833     }
25834     /** @private */
25835     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25836     
25837     /** @private */
25838     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25839     
25840     /** @private */
25841     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25842     
25843     /** @private */
25844     this.dragSpecs = {};
25845     
25846     /**
25847      * @private The adapter to use to positon and resize elements
25848      */
25849     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25850     this.adapter.init(this);
25851     
25852     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25853         /** @private */
25854         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25855         this.el.addClass("x-splitbar-h");
25856     }else{
25857         /** @private */
25858         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25859         this.el.addClass("x-splitbar-v");
25860     }
25861     
25862     this.addEvents({
25863         /**
25864          * @event resize
25865          * Fires when the splitter is moved (alias for {@link #event-moved})
25866          * @param {Roo.SplitBar} this
25867          * @param {Number} newSize the new width or height
25868          */
25869         "resize" : true,
25870         /**
25871          * @event moved
25872          * Fires when the splitter is moved
25873          * @param {Roo.SplitBar} this
25874          * @param {Number} newSize the new width or height
25875          */
25876         "moved" : true,
25877         /**
25878          * @event beforeresize
25879          * Fires before the splitter is dragged
25880          * @param {Roo.SplitBar} this
25881          */
25882         "beforeresize" : true,
25883
25884         "beforeapply" : true
25885     });
25886
25887     Roo.util.Observable.call(this);
25888 };
25889
25890 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
25891     onStartProxyDrag : function(x, y){
25892         this.fireEvent("beforeresize", this);
25893         if(!this.overlay){
25894             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
25895             o.unselectable();
25896             o.enableDisplayMode("block");
25897             // all splitbars share the same overlay
25898             Roo.SplitBar.prototype.overlay = o;
25899         }
25900         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
25901         this.overlay.show();
25902         Roo.get(this.proxy).setDisplayed("block");
25903         var size = this.adapter.getElementSize(this);
25904         this.activeMinSize = this.getMinimumSize();;
25905         this.activeMaxSize = this.getMaximumSize();;
25906         var c1 = size - this.activeMinSize;
25907         var c2 = Math.max(this.activeMaxSize - size, 0);
25908         if(this.orientation == Roo.SplitBar.HORIZONTAL){
25909             this.dd.resetConstraints();
25910             this.dd.setXConstraint(
25911                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
25912                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
25913             );
25914             this.dd.setYConstraint(0, 0);
25915         }else{
25916             this.dd.resetConstraints();
25917             this.dd.setXConstraint(0, 0);
25918             this.dd.setYConstraint(
25919                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
25920                 this.placement == Roo.SplitBar.TOP ? c2 : c1
25921             );
25922          }
25923         this.dragSpecs.startSize = size;
25924         this.dragSpecs.startPoint = [x, y];
25925         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
25926     },
25927     
25928     /** 
25929      * @private Called after the drag operation by the DDProxy
25930      */
25931     onEndProxyDrag : function(e){
25932         Roo.get(this.proxy).setDisplayed(false);
25933         var endPoint = Roo.lib.Event.getXY(e);
25934         if(this.overlay){
25935             this.overlay.hide();
25936         }
25937         var newSize;
25938         if(this.orientation == Roo.SplitBar.HORIZONTAL){
25939             newSize = this.dragSpecs.startSize + 
25940                 (this.placement == Roo.SplitBar.LEFT ?
25941                     endPoint[0] - this.dragSpecs.startPoint[0] :
25942                     this.dragSpecs.startPoint[0] - endPoint[0]
25943                 );
25944         }else{
25945             newSize = this.dragSpecs.startSize + 
25946                 (this.placement == Roo.SplitBar.TOP ?
25947                     endPoint[1] - this.dragSpecs.startPoint[1] :
25948                     this.dragSpecs.startPoint[1] - endPoint[1]
25949                 );
25950         }
25951         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
25952         if(newSize != this.dragSpecs.startSize){
25953             if(this.fireEvent('beforeapply', this, newSize) !== false){
25954                 this.adapter.setElementSize(this, newSize);
25955                 this.fireEvent("moved", this, newSize);
25956                 this.fireEvent("resize", this, newSize);
25957             }
25958         }
25959     },
25960     
25961     /**
25962      * Get the adapter this SplitBar uses
25963      * @return The adapter object
25964      */
25965     getAdapter : function(){
25966         return this.adapter;
25967     },
25968     
25969     /**
25970      * Set the adapter this SplitBar uses
25971      * @param {Object} adapter A SplitBar adapter object
25972      */
25973     setAdapter : function(adapter){
25974         this.adapter = adapter;
25975         this.adapter.init(this);
25976     },
25977     
25978     /**
25979      * Gets the minimum size for the resizing element
25980      * @return {Number} The minimum size
25981      */
25982     getMinimumSize : function(){
25983         return this.minSize;
25984     },
25985     
25986     /**
25987      * Sets the minimum size for the resizing element
25988      * @param {Number} minSize The minimum size
25989      */
25990     setMinimumSize : function(minSize){
25991         this.minSize = minSize;
25992     },
25993     
25994     /**
25995      * Gets the maximum size for the resizing element
25996      * @return {Number} The maximum size
25997      */
25998     getMaximumSize : function(){
25999         return this.maxSize;
26000     },
26001     
26002     /**
26003      * Sets the maximum size for the resizing element
26004      * @param {Number} maxSize The maximum size
26005      */
26006     setMaximumSize : function(maxSize){
26007         this.maxSize = maxSize;
26008     },
26009     
26010     /**
26011      * Sets the initialize size for the resizing element
26012      * @param {Number} size The initial size
26013      */
26014     setCurrentSize : function(size){
26015         var oldAnimate = this.animate;
26016         this.animate = false;
26017         this.adapter.setElementSize(this, size);
26018         this.animate = oldAnimate;
26019     },
26020     
26021     /**
26022      * Destroy this splitbar. 
26023      * @param {Boolean} removeEl True to remove the element
26024      */
26025     destroy : function(removeEl){
26026         if(this.shim){
26027             this.shim.remove();
26028         }
26029         this.dd.unreg();
26030         this.proxy.parentNode.removeChild(this.proxy);
26031         if(removeEl){
26032             this.el.remove();
26033         }
26034     }
26035 });
26036
26037 /**
26038  * @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.
26039  */
26040 Roo.SplitBar.createProxy = function(dir){
26041     var proxy = new Roo.Element(document.createElement("div"));
26042     proxy.unselectable();
26043     var cls = 'x-splitbar-proxy';
26044     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26045     document.body.appendChild(proxy.dom);
26046     return proxy.dom;
26047 };
26048
26049 /** 
26050  * @class Roo.SplitBar.BasicLayoutAdapter
26051  * Default Adapter. It assumes the splitter and resizing element are not positioned
26052  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26053  */
26054 Roo.SplitBar.BasicLayoutAdapter = function(){
26055 };
26056
26057 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26058     // do nothing for now
26059     init : function(s){
26060     
26061     },
26062     /**
26063      * Called before drag operations to get the current size of the resizing element. 
26064      * @param {Roo.SplitBar} s The SplitBar using this adapter
26065      */
26066      getElementSize : function(s){
26067         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26068             return s.resizingEl.getWidth();
26069         }else{
26070             return s.resizingEl.getHeight();
26071         }
26072     },
26073     
26074     /**
26075      * Called after drag operations to set the size of the resizing element.
26076      * @param {Roo.SplitBar} s The SplitBar using this adapter
26077      * @param {Number} newSize The new size to set
26078      * @param {Function} onComplete A function to be invoked when resizing is complete
26079      */
26080     setElementSize : function(s, newSize, onComplete){
26081         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26082             if(!s.animate){
26083                 s.resizingEl.setWidth(newSize);
26084                 if(onComplete){
26085                     onComplete(s, newSize);
26086                 }
26087             }else{
26088                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26089             }
26090         }else{
26091             
26092             if(!s.animate){
26093                 s.resizingEl.setHeight(newSize);
26094                 if(onComplete){
26095                     onComplete(s, newSize);
26096                 }
26097             }else{
26098                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26099             }
26100         }
26101     }
26102 };
26103
26104 /** 
26105  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26106  * @extends Roo.SplitBar.BasicLayoutAdapter
26107  * Adapter that  moves the splitter element to align with the resized sizing element. 
26108  * Used with an absolute positioned SplitBar.
26109  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26110  * document.body, make sure you assign an id to the body element.
26111  */
26112 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26113     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26114     this.container = Roo.get(container);
26115 };
26116
26117 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26118     init : function(s){
26119         this.basic.init(s);
26120     },
26121     
26122     getElementSize : function(s){
26123         return this.basic.getElementSize(s);
26124     },
26125     
26126     setElementSize : function(s, newSize, onComplete){
26127         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26128     },
26129     
26130     moveSplitter : function(s){
26131         var yes = Roo.SplitBar;
26132         switch(s.placement){
26133             case yes.LEFT:
26134                 s.el.setX(s.resizingEl.getRight());
26135                 break;
26136             case yes.RIGHT:
26137                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26138                 break;
26139             case yes.TOP:
26140                 s.el.setY(s.resizingEl.getBottom());
26141                 break;
26142             case yes.BOTTOM:
26143                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26144                 break;
26145         }
26146     }
26147 };
26148
26149 /**
26150  * Orientation constant - Create a vertical SplitBar
26151  * @static
26152  * @type Number
26153  */
26154 Roo.SplitBar.VERTICAL = 1;
26155
26156 /**
26157  * Orientation constant - Create a horizontal SplitBar
26158  * @static
26159  * @type Number
26160  */
26161 Roo.SplitBar.HORIZONTAL = 2;
26162
26163 /**
26164  * Placement constant - The resizing element is to the left of the splitter element
26165  * @static
26166  * @type Number
26167  */
26168 Roo.SplitBar.LEFT = 1;
26169
26170 /**
26171  * Placement constant - The resizing element is to the right of the splitter element
26172  * @static
26173  * @type Number
26174  */
26175 Roo.SplitBar.RIGHT = 2;
26176
26177 /**
26178  * Placement constant - The resizing element is positioned above the splitter element
26179  * @static
26180  * @type Number
26181  */
26182 Roo.SplitBar.TOP = 3;
26183
26184 /**
26185  * Placement constant - The resizing element is positioned under splitter element
26186  * @static
26187  * @type Number
26188  */
26189 Roo.SplitBar.BOTTOM = 4;
26190 /*
26191  * Based on:
26192  * Ext JS Library 1.1.1
26193  * Copyright(c) 2006-2007, Ext JS, LLC.
26194  *
26195  * Originally Released Under LGPL - original licence link has changed is not relivant.
26196  *
26197  * Fork - LGPL
26198  * <script type="text/javascript">
26199  */
26200
26201 /**
26202  * @class Roo.View
26203  * @extends Roo.util.Observable
26204  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26205  * This class also supports single and multi selection modes. <br>
26206  * Create a data model bound view:
26207  <pre><code>
26208  var store = new Roo.data.Store(...);
26209
26210  var view = new Roo.View({
26211     el : "my-element",
26212     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26213  
26214     singleSelect: true,
26215     selectedClass: "ydataview-selected",
26216     store: store
26217  });
26218
26219  // listen for node click?
26220  view.on("click", function(vw, index, node, e){
26221  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26222  });
26223
26224  // load XML data
26225  dataModel.load("foobar.xml");
26226  </code></pre>
26227  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26228  * <br><br>
26229  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26230  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26231  * 
26232  * Note: old style constructor is still suported (container, template, config)
26233  * 
26234  * @constructor
26235  * Create a new View
26236  * @param {Object} config The config object
26237  * 
26238  */
26239 Roo.View = function(config, depreciated_tpl, depreciated_config){
26240     
26241     this.parent = false;
26242     
26243     if (typeof(depreciated_tpl) == 'undefined') {
26244         // new way.. - universal constructor.
26245         Roo.apply(this, config);
26246         this.el  = Roo.get(this.el);
26247     } else {
26248         // old format..
26249         this.el  = Roo.get(config);
26250         this.tpl = depreciated_tpl;
26251         Roo.apply(this, depreciated_config);
26252     }
26253     this.wrapEl  = this.el.wrap().wrap();
26254     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26255     
26256     
26257     if(typeof(this.tpl) == "string"){
26258         this.tpl = new Roo.Template(this.tpl);
26259     } else {
26260         // support xtype ctors..
26261         this.tpl = new Roo.factory(this.tpl, Roo);
26262     }
26263     
26264     
26265     this.tpl.compile();
26266     
26267     /** @private */
26268     this.addEvents({
26269         /**
26270          * @event beforeclick
26271          * Fires before a click is processed. Returns false to cancel the default action.
26272          * @param {Roo.View} this
26273          * @param {Number} index The index of the target node
26274          * @param {HTMLElement} node The target node
26275          * @param {Roo.EventObject} e The raw event object
26276          */
26277             "beforeclick" : true,
26278         /**
26279          * @event click
26280          * Fires when a template node is clicked.
26281          * @param {Roo.View} this
26282          * @param {Number} index The index of the target node
26283          * @param {HTMLElement} node The target node
26284          * @param {Roo.EventObject} e The raw event object
26285          */
26286             "click" : true,
26287         /**
26288          * @event dblclick
26289          * Fires when a template node is double clicked.
26290          * @param {Roo.View} this
26291          * @param {Number} index The index of the target node
26292          * @param {HTMLElement} node The target node
26293          * @param {Roo.EventObject} e The raw event object
26294          */
26295             "dblclick" : true,
26296         /**
26297          * @event contextmenu
26298          * Fires when a template node is right clicked.
26299          * @param {Roo.View} this
26300          * @param {Number} index The index of the target node
26301          * @param {HTMLElement} node The target node
26302          * @param {Roo.EventObject} e The raw event object
26303          */
26304             "contextmenu" : true,
26305         /**
26306          * @event selectionchange
26307          * Fires when the selected nodes change.
26308          * @param {Roo.View} this
26309          * @param {Array} selections Array of the selected nodes
26310          */
26311             "selectionchange" : true,
26312     
26313         /**
26314          * @event beforeselect
26315          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26316          * @param {Roo.View} this
26317          * @param {HTMLElement} node The node to be selected
26318          * @param {Array} selections Array of currently selected nodes
26319          */
26320             "beforeselect" : true,
26321         /**
26322          * @event preparedata
26323          * Fires on every row to render, to allow you to change the data.
26324          * @param {Roo.View} this
26325          * @param {Object} data to be rendered (change this)
26326          */
26327           "preparedata" : true
26328           
26329           
26330         });
26331
26332
26333
26334     this.el.on({
26335         "click": this.onClick,
26336         "dblclick": this.onDblClick,
26337         "contextmenu": this.onContextMenu,
26338         scope:this
26339     });
26340
26341     this.selections = [];
26342     this.nodes = [];
26343     this.cmp = new Roo.CompositeElementLite([]);
26344     if(this.store){
26345         this.store = Roo.factory(this.store, Roo.data);
26346         this.setStore(this.store, true);
26347     }
26348     
26349     if ( this.footer && this.footer.xtype) {
26350            
26351          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26352         
26353         this.footer.dataSource = this.store;
26354         this.footer.container = fctr;
26355         this.footer = Roo.factory(this.footer, Roo);
26356         fctr.insertFirst(this.el);
26357         
26358         // this is a bit insane - as the paging toolbar seems to detach the el..
26359 //        dom.parentNode.parentNode.parentNode
26360          // they get detached?
26361     }
26362     
26363     
26364     Roo.View.superclass.constructor.call(this);
26365     
26366     
26367 };
26368
26369 Roo.extend(Roo.View, Roo.util.Observable, {
26370     
26371      /**
26372      * @cfg {Roo.data.Store} store Data store to load data from.
26373      */
26374     store : false,
26375     
26376     /**
26377      * @cfg {String|Roo.Element} el The container element.
26378      */
26379     el : '',
26380     
26381     /**
26382      * @cfg {String|Roo.Template} tpl The template used by this View 
26383      */
26384     tpl : false,
26385     /**
26386      * @cfg {String} dataName the named area of the template to use as the data area
26387      *                          Works with domtemplates roo-name="name"
26388      */
26389     dataName: false,
26390     /**
26391      * @cfg {String} selectedClass The css class to add to selected nodes
26392      */
26393     selectedClass : "x-view-selected",
26394      /**
26395      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26396      */
26397     emptyText : "",
26398     
26399     /**
26400      * @cfg {String} text to display on mask (default Loading)
26401      */
26402     mask : false,
26403     /**
26404      * @cfg {Boolean} multiSelect Allow multiple selection
26405      */
26406     multiSelect : false,
26407     /**
26408      * @cfg {Boolean} singleSelect Allow single selection
26409      */
26410     singleSelect:  false,
26411     
26412     /**
26413      * @cfg {Boolean} toggleSelect - selecting 
26414      */
26415     toggleSelect : false,
26416     
26417     /**
26418      * @cfg {Boolean} tickable - selecting 
26419      */
26420     tickable : false,
26421     
26422     /**
26423      * Returns the element this view is bound to.
26424      * @return {Roo.Element}
26425      */
26426     getEl : function(){
26427         return this.wrapEl;
26428     },
26429     
26430     
26431
26432     /**
26433      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26434      */
26435     refresh : function(){
26436         //Roo.log('refresh');
26437         var t = this.tpl;
26438         
26439         // if we are using something like 'domtemplate', then
26440         // the what gets used is:
26441         // t.applySubtemplate(NAME, data, wrapping data..)
26442         // the outer template then get' applied with
26443         //     the store 'extra data'
26444         // and the body get's added to the
26445         //      roo-name="data" node?
26446         //      <span class='roo-tpl-{name}'></span> ?????
26447         
26448         
26449         
26450         this.clearSelections();
26451         this.el.update("");
26452         var html = [];
26453         var records = this.store.getRange();
26454         if(records.length < 1) {
26455             
26456             // is this valid??  = should it render a template??
26457             
26458             this.el.update(this.emptyText);
26459             return;
26460         }
26461         var el = this.el;
26462         if (this.dataName) {
26463             this.el.update(t.apply(this.store.meta)); //????
26464             el = this.el.child('.roo-tpl-' + this.dataName);
26465         }
26466         
26467         for(var i = 0, len = records.length; i < len; i++){
26468             var data = this.prepareData(records[i].data, i, records[i]);
26469             this.fireEvent("preparedata", this, data, i, records[i]);
26470             
26471             var d = Roo.apply({}, data);
26472             
26473             if(this.tickable){
26474                 Roo.apply(d, {'roo-id' : Roo.id()});
26475                 
26476                 var _this = this;
26477             
26478                 Roo.each(this.parent.item, function(item){
26479                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26480                         return;
26481                     }
26482                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26483                 });
26484             }
26485             
26486             html[html.length] = Roo.util.Format.trim(
26487                 this.dataName ?
26488                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26489                     t.apply(d)
26490             );
26491         }
26492         
26493         
26494         
26495         el.update(html.join(""));
26496         this.nodes = el.dom.childNodes;
26497         this.updateIndexes(0);
26498     },
26499     
26500
26501     /**
26502      * Function to override to reformat the data that is sent to
26503      * the template for each node.
26504      * DEPRICATED - use the preparedata event handler.
26505      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26506      * a JSON object for an UpdateManager bound view).
26507      */
26508     prepareData : function(data, index, record)
26509     {
26510         this.fireEvent("preparedata", this, data, index, record);
26511         return data;
26512     },
26513
26514     onUpdate : function(ds, record){
26515         // Roo.log('on update');   
26516         this.clearSelections();
26517         var index = this.store.indexOf(record);
26518         var n = this.nodes[index];
26519         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26520         n.parentNode.removeChild(n);
26521         this.updateIndexes(index, index);
26522     },
26523
26524     
26525     
26526 // --------- FIXME     
26527     onAdd : function(ds, records, index)
26528     {
26529         //Roo.log(['on Add', ds, records, index] );        
26530         this.clearSelections();
26531         if(this.nodes.length == 0){
26532             this.refresh();
26533             return;
26534         }
26535         var n = this.nodes[index];
26536         for(var i = 0, len = records.length; i < len; i++){
26537             var d = this.prepareData(records[i].data, i, records[i]);
26538             if(n){
26539                 this.tpl.insertBefore(n, d);
26540             }else{
26541                 
26542                 this.tpl.append(this.el, d);
26543             }
26544         }
26545         this.updateIndexes(index);
26546     },
26547
26548     onRemove : function(ds, record, index){
26549        // Roo.log('onRemove');
26550         this.clearSelections();
26551         var el = this.dataName  ?
26552             this.el.child('.roo-tpl-' + this.dataName) :
26553             this.el; 
26554         
26555         el.dom.removeChild(this.nodes[index]);
26556         this.updateIndexes(index);
26557     },
26558
26559     /**
26560      * Refresh an individual node.
26561      * @param {Number} index
26562      */
26563     refreshNode : function(index){
26564         this.onUpdate(this.store, this.store.getAt(index));
26565     },
26566
26567     updateIndexes : function(startIndex, endIndex){
26568         var ns = this.nodes;
26569         startIndex = startIndex || 0;
26570         endIndex = endIndex || ns.length - 1;
26571         for(var i = startIndex; i <= endIndex; i++){
26572             ns[i].nodeIndex = i;
26573         }
26574     },
26575
26576     /**
26577      * Changes the data store this view uses and refresh the view.
26578      * @param {Store} store
26579      */
26580     setStore : function(store, initial){
26581         if(!initial && this.store){
26582             this.store.un("datachanged", this.refresh);
26583             this.store.un("add", this.onAdd);
26584             this.store.un("remove", this.onRemove);
26585             this.store.un("update", this.onUpdate);
26586             this.store.un("clear", this.refresh);
26587             this.store.un("beforeload", this.onBeforeLoad);
26588             this.store.un("load", this.onLoad);
26589             this.store.un("loadexception", this.onLoad);
26590         }
26591         if(store){
26592           
26593             store.on("datachanged", this.refresh, this);
26594             store.on("add", this.onAdd, this);
26595             store.on("remove", this.onRemove, this);
26596             store.on("update", this.onUpdate, this);
26597             store.on("clear", this.refresh, this);
26598             store.on("beforeload", this.onBeforeLoad, this);
26599             store.on("load", this.onLoad, this);
26600             store.on("loadexception", this.onLoad, this);
26601         }
26602         
26603         if(store){
26604             this.refresh();
26605         }
26606     },
26607     /**
26608      * onbeforeLoad - masks the loading area.
26609      *
26610      */
26611     onBeforeLoad : function(store,opts)
26612     {
26613          //Roo.log('onBeforeLoad');   
26614         if (!opts.add) {
26615             this.el.update("");
26616         }
26617         this.el.mask(this.mask ? this.mask : "Loading" ); 
26618     },
26619     onLoad : function ()
26620     {
26621         this.el.unmask();
26622     },
26623     
26624
26625     /**
26626      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26627      * @param {HTMLElement} node
26628      * @return {HTMLElement} The template node
26629      */
26630     findItemFromChild : function(node){
26631         var el = this.dataName  ?
26632             this.el.child('.roo-tpl-' + this.dataName,true) :
26633             this.el.dom; 
26634         
26635         if(!node || node.parentNode == el){
26636                     return node;
26637             }
26638             var p = node.parentNode;
26639             while(p && p != el){
26640             if(p.parentNode == el){
26641                 return p;
26642             }
26643             p = p.parentNode;
26644         }
26645             return null;
26646     },
26647
26648     /** @ignore */
26649     onClick : function(e){
26650         var item = this.findItemFromChild(e.getTarget());
26651         if(item){
26652             var index = this.indexOf(item);
26653             if(this.onItemClick(item, index, e) !== false){
26654                 this.fireEvent("click", this, index, item, e);
26655             }
26656         }else{
26657             this.clearSelections();
26658         }
26659     },
26660
26661     /** @ignore */
26662     onContextMenu : function(e){
26663         var item = this.findItemFromChild(e.getTarget());
26664         if(item){
26665             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26666         }
26667     },
26668
26669     /** @ignore */
26670     onDblClick : function(e){
26671         var item = this.findItemFromChild(e.getTarget());
26672         if(item){
26673             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26674         }
26675     },
26676
26677     onItemClick : function(item, index, e)
26678     {
26679         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26680             return false;
26681         }
26682         if (this.toggleSelect) {
26683             var m = this.isSelected(item) ? 'unselect' : 'select';
26684             //Roo.log(m);
26685             var _t = this;
26686             _t[m](item, true, false);
26687             return true;
26688         }
26689         if(this.multiSelect || this.singleSelect){
26690             if(this.multiSelect && e.shiftKey && this.lastSelection){
26691                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26692             }else{
26693                 this.select(item, this.multiSelect && e.ctrlKey);
26694                 this.lastSelection = item;
26695             }
26696             
26697             if(!this.tickable){
26698                 e.preventDefault();
26699             }
26700             
26701         }
26702         return true;
26703     },
26704
26705     /**
26706      * Get the number of selected nodes.
26707      * @return {Number}
26708      */
26709     getSelectionCount : function(){
26710         return this.selections.length;
26711     },
26712
26713     /**
26714      * Get the currently selected nodes.
26715      * @return {Array} An array of HTMLElements
26716      */
26717     getSelectedNodes : function(){
26718         return this.selections;
26719     },
26720
26721     /**
26722      * Get the indexes of the selected nodes.
26723      * @return {Array}
26724      */
26725     getSelectedIndexes : function(){
26726         var indexes = [], s = this.selections;
26727         for(var i = 0, len = s.length; i < len; i++){
26728             indexes.push(s[i].nodeIndex);
26729         }
26730         return indexes;
26731     },
26732
26733     /**
26734      * Clear all selections
26735      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26736      */
26737     clearSelections : function(suppressEvent){
26738         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26739             this.cmp.elements = this.selections;
26740             this.cmp.removeClass(this.selectedClass);
26741             this.selections = [];
26742             if(!suppressEvent){
26743                 this.fireEvent("selectionchange", this, this.selections);
26744             }
26745         }
26746     },
26747
26748     /**
26749      * Returns true if the passed node is selected
26750      * @param {HTMLElement/Number} node The node or node index
26751      * @return {Boolean}
26752      */
26753     isSelected : function(node){
26754         var s = this.selections;
26755         if(s.length < 1){
26756             return false;
26757         }
26758         node = this.getNode(node);
26759         return s.indexOf(node) !== -1;
26760     },
26761
26762     /**
26763      * Selects nodes.
26764      * @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
26765      * @param {Boolean} keepExisting (optional) true to keep existing selections
26766      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26767      */
26768     select : function(nodeInfo, keepExisting, suppressEvent){
26769         if(nodeInfo instanceof Array){
26770             if(!keepExisting){
26771                 this.clearSelections(true);
26772             }
26773             for(var i = 0, len = nodeInfo.length; i < len; i++){
26774                 this.select(nodeInfo[i], true, true);
26775             }
26776             return;
26777         } 
26778         var node = this.getNode(nodeInfo);
26779         if(!node || this.isSelected(node)){
26780             return; // already selected.
26781         }
26782         if(!keepExisting){
26783             this.clearSelections(true);
26784         }
26785         
26786         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26787             Roo.fly(node).addClass(this.selectedClass);
26788             this.selections.push(node);
26789             if(!suppressEvent){
26790                 this.fireEvent("selectionchange", this, this.selections);
26791             }
26792         }
26793         
26794         
26795     },
26796       /**
26797      * Unselects nodes.
26798      * @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
26799      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26800      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26801      */
26802     unselect : function(nodeInfo, keepExisting, suppressEvent)
26803     {
26804         if(nodeInfo instanceof Array){
26805             Roo.each(this.selections, function(s) {
26806                 this.unselect(s, nodeInfo);
26807             }, this);
26808             return;
26809         }
26810         var node = this.getNode(nodeInfo);
26811         if(!node || !this.isSelected(node)){
26812             //Roo.log("not selected");
26813             return; // not selected.
26814         }
26815         // fireevent???
26816         var ns = [];
26817         Roo.each(this.selections, function(s) {
26818             if (s == node ) {
26819                 Roo.fly(node).removeClass(this.selectedClass);
26820
26821                 return;
26822             }
26823             ns.push(s);
26824         },this);
26825         
26826         this.selections= ns;
26827         this.fireEvent("selectionchange", this, this.selections);
26828     },
26829
26830     /**
26831      * Gets a template node.
26832      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26833      * @return {HTMLElement} The node or null if it wasn't found
26834      */
26835     getNode : function(nodeInfo){
26836         if(typeof nodeInfo == "string"){
26837             return document.getElementById(nodeInfo);
26838         }else if(typeof nodeInfo == "number"){
26839             return this.nodes[nodeInfo];
26840         }
26841         return nodeInfo;
26842     },
26843
26844     /**
26845      * Gets a range template nodes.
26846      * @param {Number} startIndex
26847      * @param {Number} endIndex
26848      * @return {Array} An array of nodes
26849      */
26850     getNodes : function(start, end){
26851         var ns = this.nodes;
26852         start = start || 0;
26853         end = typeof end == "undefined" ? ns.length - 1 : end;
26854         var nodes = [];
26855         if(start <= end){
26856             for(var i = start; i <= end; i++){
26857                 nodes.push(ns[i]);
26858             }
26859         } else{
26860             for(var i = start; i >= end; i--){
26861                 nodes.push(ns[i]);
26862             }
26863         }
26864         return nodes;
26865     },
26866
26867     /**
26868      * Finds the index of the passed node
26869      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26870      * @return {Number} The index of the node or -1
26871      */
26872     indexOf : function(node){
26873         node = this.getNode(node);
26874         if(typeof node.nodeIndex == "number"){
26875             return node.nodeIndex;
26876         }
26877         var ns = this.nodes;
26878         for(var i = 0, len = ns.length; i < len; i++){
26879             if(ns[i] == node){
26880                 return i;
26881             }
26882         }
26883         return -1;
26884     }
26885 });
26886 /*
26887  * Based on:
26888  * Ext JS Library 1.1.1
26889  * Copyright(c) 2006-2007, Ext JS, LLC.
26890  *
26891  * Originally Released Under LGPL - original licence link has changed is not relivant.
26892  *
26893  * Fork - LGPL
26894  * <script type="text/javascript">
26895  */
26896
26897 /**
26898  * @class Roo.JsonView
26899  * @extends Roo.View
26900  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
26901 <pre><code>
26902 var view = new Roo.JsonView({
26903     container: "my-element",
26904     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
26905     multiSelect: true, 
26906     jsonRoot: "data" 
26907 });
26908
26909 // listen for node click?
26910 view.on("click", function(vw, index, node, e){
26911     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26912 });
26913
26914 // direct load of JSON data
26915 view.load("foobar.php");
26916
26917 // Example from my blog list
26918 var tpl = new Roo.Template(
26919     '&lt;div class="entry"&gt;' +
26920     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
26921     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
26922     "&lt;/div&gt;&lt;hr /&gt;"
26923 );
26924
26925 var moreView = new Roo.JsonView({
26926     container :  "entry-list", 
26927     template : tpl,
26928     jsonRoot: "posts"
26929 });
26930 moreView.on("beforerender", this.sortEntries, this);
26931 moreView.load({
26932     url: "/blog/get-posts.php",
26933     params: "allposts=true",
26934     text: "Loading Blog Entries..."
26935 });
26936 </code></pre>
26937
26938 * Note: old code is supported with arguments : (container, template, config)
26939
26940
26941  * @constructor
26942  * Create a new JsonView
26943  * 
26944  * @param {Object} config The config object
26945  * 
26946  */
26947 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
26948     
26949     
26950     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
26951
26952     var um = this.el.getUpdateManager();
26953     um.setRenderer(this);
26954     um.on("update", this.onLoad, this);
26955     um.on("failure", this.onLoadException, this);
26956
26957     /**
26958      * @event beforerender
26959      * Fires before rendering of the downloaded JSON data.
26960      * @param {Roo.JsonView} this
26961      * @param {Object} data The JSON data loaded
26962      */
26963     /**
26964      * @event load
26965      * Fires when data is loaded.
26966      * @param {Roo.JsonView} this
26967      * @param {Object} data The JSON data loaded
26968      * @param {Object} response The raw Connect response object
26969      */
26970     /**
26971      * @event loadexception
26972      * Fires when loading fails.
26973      * @param {Roo.JsonView} this
26974      * @param {Object} response The raw Connect response object
26975      */
26976     this.addEvents({
26977         'beforerender' : true,
26978         'load' : true,
26979         'loadexception' : true
26980     });
26981 };
26982 Roo.extend(Roo.JsonView, Roo.View, {
26983     /**
26984      * @type {String} The root property in the loaded JSON object that contains the data
26985      */
26986     jsonRoot : "",
26987
26988     /**
26989      * Refreshes the view.
26990      */
26991     refresh : function(){
26992         this.clearSelections();
26993         this.el.update("");
26994         var html = [];
26995         var o = this.jsonData;
26996         if(o && o.length > 0){
26997             for(var i = 0, len = o.length; i < len; i++){
26998                 var data = this.prepareData(o[i], i, o);
26999                 html[html.length] = this.tpl.apply(data);
27000             }
27001         }else{
27002             html.push(this.emptyText);
27003         }
27004         this.el.update(html.join(""));
27005         this.nodes = this.el.dom.childNodes;
27006         this.updateIndexes(0);
27007     },
27008
27009     /**
27010      * 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.
27011      * @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:
27012      <pre><code>
27013      view.load({
27014          url: "your-url.php",
27015          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27016          callback: yourFunction,
27017          scope: yourObject, //(optional scope)
27018          discardUrl: false,
27019          nocache: false,
27020          text: "Loading...",
27021          timeout: 30,
27022          scripts: false
27023      });
27024      </code></pre>
27025      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27026      * 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.
27027      * @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}
27028      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27029      * @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.
27030      */
27031     load : function(){
27032         var um = this.el.getUpdateManager();
27033         um.update.apply(um, arguments);
27034     },
27035
27036     render : function(el, response){
27037         this.clearSelections();
27038         this.el.update("");
27039         var o;
27040         try{
27041             o = Roo.util.JSON.decode(response.responseText);
27042             if(this.jsonRoot){
27043                 
27044                 o = o[this.jsonRoot];
27045             }
27046         } catch(e){
27047         }
27048         /**
27049          * The current JSON data or null
27050          */
27051         this.jsonData = o;
27052         this.beforeRender();
27053         this.refresh();
27054     },
27055
27056 /**
27057  * Get the number of records in the current JSON dataset
27058  * @return {Number}
27059  */
27060     getCount : function(){
27061         return this.jsonData ? this.jsonData.length : 0;
27062     },
27063
27064 /**
27065  * Returns the JSON object for the specified node(s)
27066  * @param {HTMLElement/Array} node The node or an array of nodes
27067  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27068  * you get the JSON object for the node
27069  */
27070     getNodeData : function(node){
27071         if(node instanceof Array){
27072             var data = [];
27073             for(var i = 0, len = node.length; i < len; i++){
27074                 data.push(this.getNodeData(node[i]));
27075             }
27076             return data;
27077         }
27078         return this.jsonData[this.indexOf(node)] || null;
27079     },
27080
27081     beforeRender : function(){
27082         this.snapshot = this.jsonData;
27083         if(this.sortInfo){
27084             this.sort.apply(this, this.sortInfo);
27085         }
27086         this.fireEvent("beforerender", this, this.jsonData);
27087     },
27088
27089     onLoad : function(el, o){
27090         this.fireEvent("load", this, this.jsonData, o);
27091     },
27092
27093     onLoadException : function(el, o){
27094         this.fireEvent("loadexception", this, o);
27095     },
27096
27097 /**
27098  * Filter the data by a specific property.
27099  * @param {String} property A property on your JSON objects
27100  * @param {String/RegExp} value Either string that the property values
27101  * should start with, or a RegExp to test against the property
27102  */
27103     filter : function(property, value){
27104         if(this.jsonData){
27105             var data = [];
27106             var ss = this.snapshot;
27107             if(typeof value == "string"){
27108                 var vlen = value.length;
27109                 if(vlen == 0){
27110                     this.clearFilter();
27111                     return;
27112                 }
27113                 value = value.toLowerCase();
27114                 for(var i = 0, len = ss.length; i < len; i++){
27115                     var o = ss[i];
27116                     if(o[property].substr(0, vlen).toLowerCase() == value){
27117                         data.push(o);
27118                     }
27119                 }
27120             } else if(value.exec){ // regex?
27121                 for(var i = 0, len = ss.length; i < len; i++){
27122                     var o = ss[i];
27123                     if(value.test(o[property])){
27124                         data.push(o);
27125                     }
27126                 }
27127             } else{
27128                 return;
27129             }
27130             this.jsonData = data;
27131             this.refresh();
27132         }
27133     },
27134
27135 /**
27136  * Filter by a function. The passed function will be called with each
27137  * object in the current dataset. If the function returns true the value is kept,
27138  * otherwise it is filtered.
27139  * @param {Function} fn
27140  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27141  */
27142     filterBy : function(fn, scope){
27143         if(this.jsonData){
27144             var data = [];
27145             var ss = this.snapshot;
27146             for(var i = 0, len = ss.length; i < len; i++){
27147                 var o = ss[i];
27148                 if(fn.call(scope || this, o)){
27149                     data.push(o);
27150                 }
27151             }
27152             this.jsonData = data;
27153             this.refresh();
27154         }
27155     },
27156
27157 /**
27158  * Clears the current filter.
27159  */
27160     clearFilter : function(){
27161         if(this.snapshot && this.jsonData != this.snapshot){
27162             this.jsonData = this.snapshot;
27163             this.refresh();
27164         }
27165     },
27166
27167
27168 /**
27169  * Sorts the data for this view and refreshes it.
27170  * @param {String} property A property on your JSON objects to sort on
27171  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27172  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27173  */
27174     sort : function(property, dir, sortType){
27175         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27176         if(this.jsonData){
27177             var p = property;
27178             var dsc = dir && dir.toLowerCase() == "desc";
27179             var f = function(o1, o2){
27180                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27181                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27182                 ;
27183                 if(v1 < v2){
27184                     return dsc ? +1 : -1;
27185                 } else if(v1 > v2){
27186                     return dsc ? -1 : +1;
27187                 } else{
27188                     return 0;
27189                 }
27190             };
27191             this.jsonData.sort(f);
27192             this.refresh();
27193             if(this.jsonData != this.snapshot){
27194                 this.snapshot.sort(f);
27195             }
27196         }
27197     }
27198 });/*
27199  * Based on:
27200  * Ext JS Library 1.1.1
27201  * Copyright(c) 2006-2007, Ext JS, LLC.
27202  *
27203  * Originally Released Under LGPL - original licence link has changed is not relivant.
27204  *
27205  * Fork - LGPL
27206  * <script type="text/javascript">
27207  */
27208  
27209
27210 /**
27211  * @class Roo.ColorPalette
27212  * @extends Roo.Component
27213  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27214  * Here's an example of typical usage:
27215  * <pre><code>
27216 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27217 cp.render('my-div');
27218
27219 cp.on('select', function(palette, selColor){
27220     // do something with selColor
27221 });
27222 </code></pre>
27223  * @constructor
27224  * Create a new ColorPalette
27225  * @param {Object} config The config object
27226  */
27227 Roo.ColorPalette = function(config){
27228     Roo.ColorPalette.superclass.constructor.call(this, config);
27229     this.addEvents({
27230         /**
27231              * @event select
27232              * Fires when a color is selected
27233              * @param {ColorPalette} this
27234              * @param {String} color The 6-digit color hex code (without the # symbol)
27235              */
27236         select: true
27237     });
27238
27239     if(this.handler){
27240         this.on("select", this.handler, this.scope, true);
27241     }
27242 };
27243 Roo.extend(Roo.ColorPalette, Roo.Component, {
27244     /**
27245      * @cfg {String} itemCls
27246      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27247      */
27248     itemCls : "x-color-palette",
27249     /**
27250      * @cfg {String} value
27251      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27252      * the hex codes are case-sensitive.
27253      */
27254     value : null,
27255     clickEvent:'click',
27256     // private
27257     ctype: "Roo.ColorPalette",
27258
27259     /**
27260      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27261      */
27262     allowReselect : false,
27263
27264     /**
27265      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27266      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27267      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27268      * of colors with the width setting until the box is symmetrical.</p>
27269      * <p>You can override individual colors if needed:</p>
27270      * <pre><code>
27271 var cp = new Roo.ColorPalette();
27272 cp.colors[0] = "FF0000";  // change the first box to red
27273 </code></pre>
27274
27275 Or you can provide a custom array of your own for complete control:
27276 <pre><code>
27277 var cp = new Roo.ColorPalette();
27278 cp.colors = ["000000", "993300", "333300"];
27279 </code></pre>
27280      * @type Array
27281      */
27282     colors : [
27283         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27284         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27285         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27286         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27287         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27288     ],
27289
27290     // private
27291     onRender : function(container, position){
27292         var t = new Roo.MasterTemplate(
27293             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27294         );
27295         var c = this.colors;
27296         for(var i = 0, len = c.length; i < len; i++){
27297             t.add([c[i]]);
27298         }
27299         var el = document.createElement("div");
27300         el.className = this.itemCls;
27301         t.overwrite(el);
27302         container.dom.insertBefore(el, position);
27303         this.el = Roo.get(el);
27304         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27305         if(this.clickEvent != 'click'){
27306             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27307         }
27308     },
27309
27310     // private
27311     afterRender : function(){
27312         Roo.ColorPalette.superclass.afterRender.call(this);
27313         if(this.value){
27314             var s = this.value;
27315             this.value = null;
27316             this.select(s);
27317         }
27318     },
27319
27320     // private
27321     handleClick : function(e, t){
27322         e.preventDefault();
27323         if(!this.disabled){
27324             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27325             this.select(c.toUpperCase());
27326         }
27327     },
27328
27329     /**
27330      * Selects the specified color in the palette (fires the select event)
27331      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27332      */
27333     select : function(color){
27334         color = color.replace("#", "");
27335         if(color != this.value || this.allowReselect){
27336             var el = this.el;
27337             if(this.value){
27338                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27339             }
27340             el.child("a.color-"+color).addClass("x-color-palette-sel");
27341             this.value = color;
27342             this.fireEvent("select", this, color);
27343         }
27344     }
27345 });/*
27346  * Based on:
27347  * Ext JS Library 1.1.1
27348  * Copyright(c) 2006-2007, Ext JS, LLC.
27349  *
27350  * Originally Released Under LGPL - original licence link has changed is not relivant.
27351  *
27352  * Fork - LGPL
27353  * <script type="text/javascript">
27354  */
27355  
27356 /**
27357  * @class Roo.DatePicker
27358  * @extends Roo.Component
27359  * Simple date picker class.
27360  * @constructor
27361  * Create a new DatePicker
27362  * @param {Object} config The config object
27363  */
27364 Roo.DatePicker = function(config){
27365     Roo.DatePicker.superclass.constructor.call(this, config);
27366
27367     this.value = config && config.value ?
27368                  config.value.clearTime() : new Date().clearTime();
27369
27370     this.addEvents({
27371         /**
27372              * @event select
27373              * Fires when a date is selected
27374              * @param {DatePicker} this
27375              * @param {Date} date The selected date
27376              */
27377         'select': true,
27378         /**
27379              * @event monthchange
27380              * Fires when the displayed month changes 
27381              * @param {DatePicker} this
27382              * @param {Date} date The selected month
27383              */
27384         'monthchange': true
27385     });
27386
27387     if(this.handler){
27388         this.on("select", this.handler,  this.scope || this);
27389     }
27390     // build the disabledDatesRE
27391     if(!this.disabledDatesRE && this.disabledDates){
27392         var dd = this.disabledDates;
27393         var re = "(?:";
27394         for(var i = 0; i < dd.length; i++){
27395             re += dd[i];
27396             if(i != dd.length-1) {
27397                 re += "|";
27398             }
27399         }
27400         this.disabledDatesRE = new RegExp(re + ")");
27401     }
27402 };
27403
27404 Roo.extend(Roo.DatePicker, Roo.Component, {
27405     /**
27406      * @cfg {String} todayText
27407      * The text to display on the button that selects the current date (defaults to "Today")
27408      */
27409     todayText : "Today",
27410     /**
27411      * @cfg {String} okText
27412      * The text to display on the ok button
27413      */
27414     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27415     /**
27416      * @cfg {String} cancelText
27417      * The text to display on the cancel button
27418      */
27419     cancelText : "Cancel",
27420     /**
27421      * @cfg {String} todayTip
27422      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27423      */
27424     todayTip : "{0} (Spacebar)",
27425     /**
27426      * @cfg {Date} minDate
27427      * Minimum allowable date (JavaScript date object, defaults to null)
27428      */
27429     minDate : null,
27430     /**
27431      * @cfg {Date} maxDate
27432      * Maximum allowable date (JavaScript date object, defaults to null)
27433      */
27434     maxDate : null,
27435     /**
27436      * @cfg {String} minText
27437      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27438      */
27439     minText : "This date is before the minimum date",
27440     /**
27441      * @cfg {String} maxText
27442      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27443      */
27444     maxText : "This date is after the maximum date",
27445     /**
27446      * @cfg {String} format
27447      * The default date format string which can be overriden for localization support.  The format must be
27448      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27449      */
27450     format : "m/d/y",
27451     /**
27452      * @cfg {Array} disabledDays
27453      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27454      */
27455     disabledDays : null,
27456     /**
27457      * @cfg {String} disabledDaysText
27458      * The tooltip to display when the date falls on a disabled day (defaults to "")
27459      */
27460     disabledDaysText : "",
27461     /**
27462      * @cfg {RegExp} disabledDatesRE
27463      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27464      */
27465     disabledDatesRE : null,
27466     /**
27467      * @cfg {String} disabledDatesText
27468      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27469      */
27470     disabledDatesText : "",
27471     /**
27472      * @cfg {Boolean} constrainToViewport
27473      * True to constrain the date picker to the viewport (defaults to true)
27474      */
27475     constrainToViewport : true,
27476     /**
27477      * @cfg {Array} monthNames
27478      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27479      */
27480     monthNames : Date.monthNames,
27481     /**
27482      * @cfg {Array} dayNames
27483      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27484      */
27485     dayNames : Date.dayNames,
27486     /**
27487      * @cfg {String} nextText
27488      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27489      */
27490     nextText: 'Next Month (Control+Right)',
27491     /**
27492      * @cfg {String} prevText
27493      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27494      */
27495     prevText: 'Previous Month (Control+Left)',
27496     /**
27497      * @cfg {String} monthYearText
27498      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27499      */
27500     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27501     /**
27502      * @cfg {Number} startDay
27503      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27504      */
27505     startDay : 0,
27506     /**
27507      * @cfg {Bool} showClear
27508      * Show a clear button (usefull for date form elements that can be blank.)
27509      */
27510     
27511     showClear: false,
27512     
27513     /**
27514      * Sets the value of the date field
27515      * @param {Date} value The date to set
27516      */
27517     setValue : function(value){
27518         var old = this.value;
27519         
27520         if (typeof(value) == 'string') {
27521          
27522             value = Date.parseDate(value, this.format);
27523         }
27524         if (!value) {
27525             value = new Date();
27526         }
27527         
27528         this.value = value.clearTime(true);
27529         if(this.el){
27530             this.update(this.value);
27531         }
27532     },
27533
27534     /**
27535      * Gets the current selected value of the date field
27536      * @return {Date} The selected date
27537      */
27538     getValue : function(){
27539         return this.value;
27540     },
27541
27542     // private
27543     focus : function(){
27544         if(this.el){
27545             this.update(this.activeDate);
27546         }
27547     },
27548
27549     // privateval
27550     onRender : function(container, position){
27551         
27552         var m = [
27553              '<table cellspacing="0">',
27554                 '<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>',
27555                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27556         var dn = this.dayNames;
27557         for(var i = 0; i < 7; i++){
27558             var d = this.startDay+i;
27559             if(d > 6){
27560                 d = d-7;
27561             }
27562             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27563         }
27564         m[m.length] = "</tr></thead><tbody><tr>";
27565         for(var i = 0; i < 42; i++) {
27566             if(i % 7 == 0 && i != 0){
27567                 m[m.length] = "</tr><tr>";
27568             }
27569             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27570         }
27571         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27572             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27573
27574         var el = document.createElement("div");
27575         el.className = "x-date-picker";
27576         el.innerHTML = m.join("");
27577
27578         container.dom.insertBefore(el, position);
27579
27580         this.el = Roo.get(el);
27581         this.eventEl = Roo.get(el.firstChild);
27582
27583         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27584             handler: this.showPrevMonth,
27585             scope: this,
27586             preventDefault:true,
27587             stopDefault:true
27588         });
27589
27590         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27591             handler: this.showNextMonth,
27592             scope: this,
27593             preventDefault:true,
27594             stopDefault:true
27595         });
27596
27597         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27598
27599         this.monthPicker = this.el.down('div.x-date-mp');
27600         this.monthPicker.enableDisplayMode('block');
27601         
27602         var kn = new Roo.KeyNav(this.eventEl, {
27603             "left" : function(e){
27604                 e.ctrlKey ?
27605                     this.showPrevMonth() :
27606                     this.update(this.activeDate.add("d", -1));
27607             },
27608
27609             "right" : function(e){
27610                 e.ctrlKey ?
27611                     this.showNextMonth() :
27612                     this.update(this.activeDate.add("d", 1));
27613             },
27614
27615             "up" : function(e){
27616                 e.ctrlKey ?
27617                     this.showNextYear() :
27618                     this.update(this.activeDate.add("d", -7));
27619             },
27620
27621             "down" : function(e){
27622                 e.ctrlKey ?
27623                     this.showPrevYear() :
27624                     this.update(this.activeDate.add("d", 7));
27625             },
27626
27627             "pageUp" : function(e){
27628                 this.showNextMonth();
27629             },
27630
27631             "pageDown" : function(e){
27632                 this.showPrevMonth();
27633             },
27634
27635             "enter" : function(e){
27636                 e.stopPropagation();
27637                 return true;
27638             },
27639
27640             scope : this
27641         });
27642
27643         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27644
27645         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27646
27647         this.el.unselectable();
27648         
27649         this.cells = this.el.select("table.x-date-inner tbody td");
27650         this.textNodes = this.el.query("table.x-date-inner tbody span");
27651
27652         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27653             text: "&#160;",
27654             tooltip: this.monthYearText
27655         });
27656
27657         this.mbtn.on('click', this.showMonthPicker, this);
27658         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27659
27660
27661         var today = (new Date()).dateFormat(this.format);
27662         
27663         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27664         if (this.showClear) {
27665             baseTb.add( new Roo.Toolbar.Fill());
27666         }
27667         baseTb.add({
27668             text: String.format(this.todayText, today),
27669             tooltip: String.format(this.todayTip, today),
27670             handler: this.selectToday,
27671             scope: this
27672         });
27673         
27674         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27675             
27676         //});
27677         if (this.showClear) {
27678             
27679             baseTb.add( new Roo.Toolbar.Fill());
27680             baseTb.add({
27681                 text: '&#160;',
27682                 cls: 'x-btn-icon x-btn-clear',
27683                 handler: function() {
27684                     //this.value = '';
27685                     this.fireEvent("select", this, '');
27686                 },
27687                 scope: this
27688             });
27689         }
27690         
27691         
27692         if(Roo.isIE){
27693             this.el.repaint();
27694         }
27695         this.update(this.value);
27696     },
27697
27698     createMonthPicker : function(){
27699         if(!this.monthPicker.dom.firstChild){
27700             var buf = ['<table border="0" cellspacing="0">'];
27701             for(var i = 0; i < 6; i++){
27702                 buf.push(
27703                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27704                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27705                     i == 0 ?
27706                     '<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>' :
27707                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27708                 );
27709             }
27710             buf.push(
27711                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27712                     this.okText,
27713                     '</button><button type="button" class="x-date-mp-cancel">',
27714                     this.cancelText,
27715                     '</button></td></tr>',
27716                 '</table>'
27717             );
27718             this.monthPicker.update(buf.join(''));
27719             this.monthPicker.on('click', this.onMonthClick, this);
27720             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27721
27722             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27723             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27724
27725             this.mpMonths.each(function(m, a, i){
27726                 i += 1;
27727                 if((i%2) == 0){
27728                     m.dom.xmonth = 5 + Math.round(i * .5);
27729                 }else{
27730                     m.dom.xmonth = Math.round((i-1) * .5);
27731                 }
27732             });
27733         }
27734     },
27735
27736     showMonthPicker : function(){
27737         this.createMonthPicker();
27738         var size = this.el.getSize();
27739         this.monthPicker.setSize(size);
27740         this.monthPicker.child('table').setSize(size);
27741
27742         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27743         this.updateMPMonth(this.mpSelMonth);
27744         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27745         this.updateMPYear(this.mpSelYear);
27746
27747         this.monthPicker.slideIn('t', {duration:.2});
27748     },
27749
27750     updateMPYear : function(y){
27751         this.mpyear = y;
27752         var ys = this.mpYears.elements;
27753         for(var i = 1; i <= 10; i++){
27754             var td = ys[i-1], y2;
27755             if((i%2) == 0){
27756                 y2 = y + Math.round(i * .5);
27757                 td.firstChild.innerHTML = y2;
27758                 td.xyear = y2;
27759             }else{
27760                 y2 = y - (5-Math.round(i * .5));
27761                 td.firstChild.innerHTML = y2;
27762                 td.xyear = y2;
27763             }
27764             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27765         }
27766     },
27767
27768     updateMPMonth : function(sm){
27769         this.mpMonths.each(function(m, a, i){
27770             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27771         });
27772     },
27773
27774     selectMPMonth: function(m){
27775         
27776     },
27777
27778     onMonthClick : function(e, t){
27779         e.stopEvent();
27780         var el = new Roo.Element(t), pn;
27781         if(el.is('button.x-date-mp-cancel')){
27782             this.hideMonthPicker();
27783         }
27784         else if(el.is('button.x-date-mp-ok')){
27785             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27786             this.hideMonthPicker();
27787         }
27788         else if(pn = el.up('td.x-date-mp-month', 2)){
27789             this.mpMonths.removeClass('x-date-mp-sel');
27790             pn.addClass('x-date-mp-sel');
27791             this.mpSelMonth = pn.dom.xmonth;
27792         }
27793         else if(pn = el.up('td.x-date-mp-year', 2)){
27794             this.mpYears.removeClass('x-date-mp-sel');
27795             pn.addClass('x-date-mp-sel');
27796             this.mpSelYear = pn.dom.xyear;
27797         }
27798         else if(el.is('a.x-date-mp-prev')){
27799             this.updateMPYear(this.mpyear-10);
27800         }
27801         else if(el.is('a.x-date-mp-next')){
27802             this.updateMPYear(this.mpyear+10);
27803         }
27804     },
27805
27806     onMonthDblClick : function(e, t){
27807         e.stopEvent();
27808         var el = new Roo.Element(t), pn;
27809         if(pn = el.up('td.x-date-mp-month', 2)){
27810             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27811             this.hideMonthPicker();
27812         }
27813         else if(pn = el.up('td.x-date-mp-year', 2)){
27814             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27815             this.hideMonthPicker();
27816         }
27817     },
27818
27819     hideMonthPicker : function(disableAnim){
27820         if(this.monthPicker){
27821             if(disableAnim === true){
27822                 this.monthPicker.hide();
27823             }else{
27824                 this.monthPicker.slideOut('t', {duration:.2});
27825             }
27826         }
27827     },
27828
27829     // private
27830     showPrevMonth : function(e){
27831         this.update(this.activeDate.add("mo", -1));
27832     },
27833
27834     // private
27835     showNextMonth : function(e){
27836         this.update(this.activeDate.add("mo", 1));
27837     },
27838
27839     // private
27840     showPrevYear : function(){
27841         this.update(this.activeDate.add("y", -1));
27842     },
27843
27844     // private
27845     showNextYear : function(){
27846         this.update(this.activeDate.add("y", 1));
27847     },
27848
27849     // private
27850     handleMouseWheel : function(e){
27851         var delta = e.getWheelDelta();
27852         if(delta > 0){
27853             this.showPrevMonth();
27854             e.stopEvent();
27855         } else if(delta < 0){
27856             this.showNextMonth();
27857             e.stopEvent();
27858         }
27859     },
27860
27861     // private
27862     handleDateClick : function(e, t){
27863         e.stopEvent();
27864         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
27865             this.setValue(new Date(t.dateValue));
27866             this.fireEvent("select", this, this.value);
27867         }
27868     },
27869
27870     // private
27871     selectToday : function(){
27872         this.setValue(new Date().clearTime());
27873         this.fireEvent("select", this, this.value);
27874     },
27875
27876     // private
27877     update : function(date)
27878     {
27879         var vd = this.activeDate;
27880         this.activeDate = date;
27881         if(vd && this.el){
27882             var t = date.getTime();
27883             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
27884                 this.cells.removeClass("x-date-selected");
27885                 this.cells.each(function(c){
27886                    if(c.dom.firstChild.dateValue == t){
27887                        c.addClass("x-date-selected");
27888                        setTimeout(function(){
27889                             try{c.dom.firstChild.focus();}catch(e){}
27890                        }, 50);
27891                        return false;
27892                    }
27893                 });
27894                 return;
27895             }
27896         }
27897         
27898         var days = date.getDaysInMonth();
27899         var firstOfMonth = date.getFirstDateOfMonth();
27900         var startingPos = firstOfMonth.getDay()-this.startDay;
27901
27902         if(startingPos <= this.startDay){
27903             startingPos += 7;
27904         }
27905
27906         var pm = date.add("mo", -1);
27907         var prevStart = pm.getDaysInMonth()-startingPos;
27908
27909         var cells = this.cells.elements;
27910         var textEls = this.textNodes;
27911         days += startingPos;
27912
27913         // convert everything to numbers so it's fast
27914         var day = 86400000;
27915         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
27916         var today = new Date().clearTime().getTime();
27917         var sel = date.clearTime().getTime();
27918         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
27919         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
27920         var ddMatch = this.disabledDatesRE;
27921         var ddText = this.disabledDatesText;
27922         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
27923         var ddaysText = this.disabledDaysText;
27924         var format = this.format;
27925
27926         var setCellClass = function(cal, cell){
27927             cell.title = "";
27928             var t = d.getTime();
27929             cell.firstChild.dateValue = t;
27930             if(t == today){
27931                 cell.className += " x-date-today";
27932                 cell.title = cal.todayText;
27933             }
27934             if(t == sel){
27935                 cell.className += " x-date-selected";
27936                 setTimeout(function(){
27937                     try{cell.firstChild.focus();}catch(e){}
27938                 }, 50);
27939             }
27940             // disabling
27941             if(t < min) {
27942                 cell.className = " x-date-disabled";
27943                 cell.title = cal.minText;
27944                 return;
27945             }
27946             if(t > max) {
27947                 cell.className = " x-date-disabled";
27948                 cell.title = cal.maxText;
27949                 return;
27950             }
27951             if(ddays){
27952                 if(ddays.indexOf(d.getDay()) != -1){
27953                     cell.title = ddaysText;
27954                     cell.className = " x-date-disabled";
27955                 }
27956             }
27957             if(ddMatch && format){
27958                 var fvalue = d.dateFormat(format);
27959                 if(ddMatch.test(fvalue)){
27960                     cell.title = ddText.replace("%0", fvalue);
27961                     cell.className = " x-date-disabled";
27962                 }
27963             }
27964         };
27965
27966         var i = 0;
27967         for(; i < startingPos; i++) {
27968             textEls[i].innerHTML = (++prevStart);
27969             d.setDate(d.getDate()+1);
27970             cells[i].className = "x-date-prevday";
27971             setCellClass(this, cells[i]);
27972         }
27973         for(; i < days; i++){
27974             intDay = i - startingPos + 1;
27975             textEls[i].innerHTML = (intDay);
27976             d.setDate(d.getDate()+1);
27977             cells[i].className = "x-date-active";
27978             setCellClass(this, cells[i]);
27979         }
27980         var extraDays = 0;
27981         for(; i < 42; i++) {
27982              textEls[i].innerHTML = (++extraDays);
27983              d.setDate(d.getDate()+1);
27984              cells[i].className = "x-date-nextday";
27985              setCellClass(this, cells[i]);
27986         }
27987
27988         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
27989         this.fireEvent('monthchange', this, date);
27990         
27991         if(!this.internalRender){
27992             var main = this.el.dom.firstChild;
27993             var w = main.offsetWidth;
27994             this.el.setWidth(w + this.el.getBorderWidth("lr"));
27995             Roo.fly(main).setWidth(w);
27996             this.internalRender = true;
27997             // opera does not respect the auto grow header center column
27998             // then, after it gets a width opera refuses to recalculate
27999             // without a second pass
28000             if(Roo.isOpera && !this.secondPass){
28001                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28002                 this.secondPass = true;
28003                 this.update.defer(10, this, [date]);
28004             }
28005         }
28006         
28007         
28008     }
28009 });        /*
28010  * Based on:
28011  * Ext JS Library 1.1.1
28012  * Copyright(c) 2006-2007, Ext JS, LLC.
28013  *
28014  * Originally Released Under LGPL - original licence link has changed is not relivant.
28015  *
28016  * Fork - LGPL
28017  * <script type="text/javascript">
28018  */
28019 /**
28020  * @class Roo.TabPanel
28021  * @extends Roo.util.Observable
28022  * A lightweight tab container.
28023  * <br><br>
28024  * Usage:
28025  * <pre><code>
28026 // basic tabs 1, built from existing content
28027 var tabs = new Roo.TabPanel("tabs1");
28028 tabs.addTab("script", "View Script");
28029 tabs.addTab("markup", "View Markup");
28030 tabs.activate("script");
28031
28032 // more advanced tabs, built from javascript
28033 var jtabs = new Roo.TabPanel("jtabs");
28034 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28035
28036 // set up the UpdateManager
28037 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28038 var updater = tab2.getUpdateManager();
28039 updater.setDefaultUrl("ajax1.htm");
28040 tab2.on('activate', updater.refresh, updater, true);
28041
28042 // Use setUrl for Ajax loading
28043 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28044 tab3.setUrl("ajax2.htm", null, true);
28045
28046 // Disabled tab
28047 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28048 tab4.disable();
28049
28050 jtabs.activate("jtabs-1");
28051  * </code></pre>
28052  * @constructor
28053  * Create a new TabPanel.
28054  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28055  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28056  */
28057 Roo.TabPanel = function(container, config){
28058     /**
28059     * The container element for this TabPanel.
28060     * @type Roo.Element
28061     */
28062     this.el = Roo.get(container, true);
28063     if(config){
28064         if(typeof config == "boolean"){
28065             this.tabPosition = config ? "bottom" : "top";
28066         }else{
28067             Roo.apply(this, config);
28068         }
28069     }
28070     if(this.tabPosition == "bottom"){
28071         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28072         this.el.addClass("x-tabs-bottom");
28073     }
28074     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28075     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28076     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28077     if(Roo.isIE){
28078         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28079     }
28080     if(this.tabPosition != "bottom"){
28081         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28082          * @type Roo.Element
28083          */
28084         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28085         this.el.addClass("x-tabs-top");
28086     }
28087     this.items = [];
28088
28089     this.bodyEl.setStyle("position", "relative");
28090
28091     this.active = null;
28092     this.activateDelegate = this.activate.createDelegate(this);
28093
28094     this.addEvents({
28095         /**
28096          * @event tabchange
28097          * Fires when the active tab changes
28098          * @param {Roo.TabPanel} this
28099          * @param {Roo.TabPanelItem} activePanel The new active tab
28100          */
28101         "tabchange": true,
28102         /**
28103          * @event beforetabchange
28104          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28105          * @param {Roo.TabPanel} this
28106          * @param {Object} e Set cancel to true on this object to cancel the tab change
28107          * @param {Roo.TabPanelItem} tab The tab being changed to
28108          */
28109         "beforetabchange" : true
28110     });
28111
28112     Roo.EventManager.onWindowResize(this.onResize, this);
28113     this.cpad = this.el.getPadding("lr");
28114     this.hiddenCount = 0;
28115
28116
28117     // toolbar on the tabbar support...
28118     if (this.toolbar) {
28119         var tcfg = this.toolbar;
28120         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28121         this.toolbar = new Roo.Toolbar(tcfg);
28122         if (Roo.isSafari) {
28123             var tbl = tcfg.container.child('table', true);
28124             tbl.setAttribute('width', '100%');
28125         }
28126         
28127     }
28128    
28129
28130
28131     Roo.TabPanel.superclass.constructor.call(this);
28132 };
28133
28134 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28135     /*
28136      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28137      */
28138     tabPosition : "top",
28139     /*
28140      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28141      */
28142     currentTabWidth : 0,
28143     /*
28144      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28145      */
28146     minTabWidth : 40,
28147     /*
28148      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28149      */
28150     maxTabWidth : 250,
28151     /*
28152      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28153      */
28154     preferredTabWidth : 175,
28155     /*
28156      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28157      */
28158     resizeTabs : false,
28159     /*
28160      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28161      */
28162     monitorResize : true,
28163     /*
28164      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28165      */
28166     toolbar : false,
28167
28168     /**
28169      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28170      * @param {String} id The id of the div to use <b>or create</b>
28171      * @param {String} text The text for the tab
28172      * @param {String} content (optional) Content to put in the TabPanelItem body
28173      * @param {Boolean} closable (optional) True to create a close icon on the tab
28174      * @return {Roo.TabPanelItem} The created TabPanelItem
28175      */
28176     addTab : function(id, text, content, closable){
28177         var item = new Roo.TabPanelItem(this, id, text, closable);
28178         this.addTabItem(item);
28179         if(content){
28180             item.setContent(content);
28181         }
28182         return item;
28183     },
28184
28185     /**
28186      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28187      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28188      * @return {Roo.TabPanelItem}
28189      */
28190     getTab : function(id){
28191         return this.items[id];
28192     },
28193
28194     /**
28195      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28196      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28197      */
28198     hideTab : function(id){
28199         var t = this.items[id];
28200         if(!t.isHidden()){
28201            t.setHidden(true);
28202            this.hiddenCount++;
28203            this.autoSizeTabs();
28204         }
28205     },
28206
28207     /**
28208      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28209      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28210      */
28211     unhideTab : function(id){
28212         var t = this.items[id];
28213         if(t.isHidden()){
28214            t.setHidden(false);
28215            this.hiddenCount--;
28216            this.autoSizeTabs();
28217         }
28218     },
28219
28220     /**
28221      * Adds an existing {@link Roo.TabPanelItem}.
28222      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28223      */
28224     addTabItem : function(item){
28225         this.items[item.id] = item;
28226         this.items.push(item);
28227         if(this.resizeTabs){
28228            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28229            this.autoSizeTabs();
28230         }else{
28231             item.autoSize();
28232         }
28233     },
28234
28235     /**
28236      * Removes a {@link Roo.TabPanelItem}.
28237      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28238      */
28239     removeTab : function(id){
28240         var items = this.items;
28241         var tab = items[id];
28242         if(!tab) { return; }
28243         var index = items.indexOf(tab);
28244         if(this.active == tab && items.length > 1){
28245             var newTab = this.getNextAvailable(index);
28246             if(newTab) {
28247                 newTab.activate();
28248             }
28249         }
28250         this.stripEl.dom.removeChild(tab.pnode.dom);
28251         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28252             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28253         }
28254         items.splice(index, 1);
28255         delete this.items[tab.id];
28256         tab.fireEvent("close", tab);
28257         tab.purgeListeners();
28258         this.autoSizeTabs();
28259     },
28260
28261     getNextAvailable : function(start){
28262         var items = this.items;
28263         var index = start;
28264         // look for a next tab that will slide over to
28265         // replace the one being removed
28266         while(index < items.length){
28267             var item = items[++index];
28268             if(item && !item.isHidden()){
28269                 return item;
28270             }
28271         }
28272         // if one isn't found select the previous tab (on the left)
28273         index = start;
28274         while(index >= 0){
28275             var item = items[--index];
28276             if(item && !item.isHidden()){
28277                 return item;
28278             }
28279         }
28280         return null;
28281     },
28282
28283     /**
28284      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28285      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28286      */
28287     disableTab : function(id){
28288         var tab = this.items[id];
28289         if(tab && this.active != tab){
28290             tab.disable();
28291         }
28292     },
28293
28294     /**
28295      * Enables a {@link Roo.TabPanelItem} that is disabled.
28296      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28297      */
28298     enableTab : function(id){
28299         var tab = this.items[id];
28300         tab.enable();
28301     },
28302
28303     /**
28304      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28305      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28306      * @return {Roo.TabPanelItem} The TabPanelItem.
28307      */
28308     activate : function(id){
28309         var tab = this.items[id];
28310         if(!tab){
28311             return null;
28312         }
28313         if(tab == this.active || tab.disabled){
28314             return tab;
28315         }
28316         var e = {};
28317         this.fireEvent("beforetabchange", this, e, tab);
28318         if(e.cancel !== true && !tab.disabled){
28319             if(this.active){
28320                 this.active.hide();
28321             }
28322             this.active = this.items[id];
28323             this.active.show();
28324             this.fireEvent("tabchange", this, this.active);
28325         }
28326         return tab;
28327     },
28328
28329     /**
28330      * Gets the active {@link Roo.TabPanelItem}.
28331      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28332      */
28333     getActiveTab : function(){
28334         return this.active;
28335     },
28336
28337     /**
28338      * Updates the tab body element to fit the height of the container element
28339      * for overflow scrolling
28340      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28341      */
28342     syncHeight : function(targetHeight){
28343         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28344         var bm = this.bodyEl.getMargins();
28345         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28346         this.bodyEl.setHeight(newHeight);
28347         return newHeight;
28348     },
28349
28350     onResize : function(){
28351         if(this.monitorResize){
28352             this.autoSizeTabs();
28353         }
28354     },
28355
28356     /**
28357      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28358      */
28359     beginUpdate : function(){
28360         this.updating = true;
28361     },
28362
28363     /**
28364      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28365      */
28366     endUpdate : function(){
28367         this.updating = false;
28368         this.autoSizeTabs();
28369     },
28370
28371     /**
28372      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28373      */
28374     autoSizeTabs : function(){
28375         var count = this.items.length;
28376         var vcount = count - this.hiddenCount;
28377         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28378             return;
28379         }
28380         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28381         var availWidth = Math.floor(w / vcount);
28382         var b = this.stripBody;
28383         if(b.getWidth() > w){
28384             var tabs = this.items;
28385             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28386             if(availWidth < this.minTabWidth){
28387                 /*if(!this.sleft){    // incomplete scrolling code
28388                     this.createScrollButtons();
28389                 }
28390                 this.showScroll();
28391                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28392             }
28393         }else{
28394             if(this.currentTabWidth < this.preferredTabWidth){
28395                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28396             }
28397         }
28398     },
28399
28400     /**
28401      * Returns the number of tabs in this TabPanel.
28402      * @return {Number}
28403      */
28404      getCount : function(){
28405          return this.items.length;
28406      },
28407
28408     /**
28409      * Resizes all the tabs to the passed width
28410      * @param {Number} The new width
28411      */
28412     setTabWidth : function(width){
28413         this.currentTabWidth = width;
28414         for(var i = 0, len = this.items.length; i < len; i++) {
28415                 if(!this.items[i].isHidden()) {
28416                 this.items[i].setWidth(width);
28417             }
28418         }
28419     },
28420
28421     /**
28422      * Destroys this TabPanel
28423      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28424      */
28425     destroy : function(removeEl){
28426         Roo.EventManager.removeResizeListener(this.onResize, this);
28427         for(var i = 0, len = this.items.length; i < len; i++){
28428             this.items[i].purgeListeners();
28429         }
28430         if(removeEl === true){
28431             this.el.update("");
28432             this.el.remove();
28433         }
28434     }
28435 });
28436
28437 /**
28438  * @class Roo.TabPanelItem
28439  * @extends Roo.util.Observable
28440  * Represents an individual item (tab plus body) in a TabPanel.
28441  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28442  * @param {String} id The id of this TabPanelItem
28443  * @param {String} text The text for the tab of this TabPanelItem
28444  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28445  */
28446 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28447     /**
28448      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28449      * @type Roo.TabPanel
28450      */
28451     this.tabPanel = tabPanel;
28452     /**
28453      * The id for this TabPanelItem
28454      * @type String
28455      */
28456     this.id = id;
28457     /** @private */
28458     this.disabled = false;
28459     /** @private */
28460     this.text = text;
28461     /** @private */
28462     this.loaded = false;
28463     this.closable = closable;
28464
28465     /**
28466      * The body element for this TabPanelItem.
28467      * @type Roo.Element
28468      */
28469     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28470     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28471     this.bodyEl.setStyle("display", "block");
28472     this.bodyEl.setStyle("zoom", "1");
28473     this.hideAction();
28474
28475     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28476     /** @private */
28477     this.el = Roo.get(els.el, true);
28478     this.inner = Roo.get(els.inner, true);
28479     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28480     this.pnode = Roo.get(els.el.parentNode, true);
28481     this.el.on("mousedown", this.onTabMouseDown, this);
28482     this.el.on("click", this.onTabClick, this);
28483     /** @private */
28484     if(closable){
28485         var c = Roo.get(els.close, true);
28486         c.dom.title = this.closeText;
28487         c.addClassOnOver("close-over");
28488         c.on("click", this.closeClick, this);
28489      }
28490
28491     this.addEvents({
28492          /**
28493          * @event activate
28494          * Fires when this tab becomes the active tab.
28495          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28496          * @param {Roo.TabPanelItem} this
28497          */
28498         "activate": true,
28499         /**
28500          * @event beforeclose
28501          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28502          * @param {Roo.TabPanelItem} this
28503          * @param {Object} e Set cancel to true on this object to cancel the close.
28504          */
28505         "beforeclose": true,
28506         /**
28507          * @event close
28508          * Fires when this tab is closed.
28509          * @param {Roo.TabPanelItem} this
28510          */
28511          "close": true,
28512         /**
28513          * @event deactivate
28514          * Fires when this tab is no longer the active tab.
28515          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28516          * @param {Roo.TabPanelItem} this
28517          */
28518          "deactivate" : true
28519     });
28520     this.hidden = false;
28521
28522     Roo.TabPanelItem.superclass.constructor.call(this);
28523 };
28524
28525 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28526     purgeListeners : function(){
28527        Roo.util.Observable.prototype.purgeListeners.call(this);
28528        this.el.removeAllListeners();
28529     },
28530     /**
28531      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28532      */
28533     show : function(){
28534         this.pnode.addClass("on");
28535         this.showAction();
28536         if(Roo.isOpera){
28537             this.tabPanel.stripWrap.repaint();
28538         }
28539         this.fireEvent("activate", this.tabPanel, this);
28540     },
28541
28542     /**
28543      * Returns true if this tab is the active tab.
28544      * @return {Boolean}
28545      */
28546     isActive : function(){
28547         return this.tabPanel.getActiveTab() == this;
28548     },
28549
28550     /**
28551      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28552      */
28553     hide : function(){
28554         this.pnode.removeClass("on");
28555         this.hideAction();
28556         this.fireEvent("deactivate", this.tabPanel, this);
28557     },
28558
28559     hideAction : function(){
28560         this.bodyEl.hide();
28561         this.bodyEl.setStyle("position", "absolute");
28562         this.bodyEl.setLeft("-20000px");
28563         this.bodyEl.setTop("-20000px");
28564     },
28565
28566     showAction : function(){
28567         this.bodyEl.setStyle("position", "relative");
28568         this.bodyEl.setTop("");
28569         this.bodyEl.setLeft("");
28570         this.bodyEl.show();
28571     },
28572
28573     /**
28574      * Set the tooltip for the tab.
28575      * @param {String} tooltip The tab's tooltip
28576      */
28577     setTooltip : function(text){
28578         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28579             this.textEl.dom.qtip = text;
28580             this.textEl.dom.removeAttribute('title');
28581         }else{
28582             this.textEl.dom.title = text;
28583         }
28584     },
28585
28586     onTabClick : function(e){
28587         e.preventDefault();
28588         this.tabPanel.activate(this.id);
28589     },
28590
28591     onTabMouseDown : function(e){
28592         e.preventDefault();
28593         this.tabPanel.activate(this.id);
28594     },
28595
28596     getWidth : function(){
28597         return this.inner.getWidth();
28598     },
28599
28600     setWidth : function(width){
28601         var iwidth = width - this.pnode.getPadding("lr");
28602         this.inner.setWidth(iwidth);
28603         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28604         this.pnode.setWidth(width);
28605     },
28606
28607     /**
28608      * Show or hide the tab
28609      * @param {Boolean} hidden True to hide or false to show.
28610      */
28611     setHidden : function(hidden){
28612         this.hidden = hidden;
28613         this.pnode.setStyle("display", hidden ? "none" : "");
28614     },
28615
28616     /**
28617      * Returns true if this tab is "hidden"
28618      * @return {Boolean}
28619      */
28620     isHidden : function(){
28621         return this.hidden;
28622     },
28623
28624     /**
28625      * Returns the text for this tab
28626      * @return {String}
28627      */
28628     getText : function(){
28629         return this.text;
28630     },
28631
28632     autoSize : function(){
28633         //this.el.beginMeasure();
28634         this.textEl.setWidth(1);
28635         /*
28636          *  #2804 [new] Tabs in Roojs
28637          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28638          */
28639         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28640         //this.el.endMeasure();
28641     },
28642
28643     /**
28644      * Sets the text for the tab (Note: this also sets the tooltip text)
28645      * @param {String} text The tab's text and tooltip
28646      */
28647     setText : function(text){
28648         this.text = text;
28649         this.textEl.update(text);
28650         this.setTooltip(text);
28651         if(!this.tabPanel.resizeTabs){
28652             this.autoSize();
28653         }
28654     },
28655     /**
28656      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28657      */
28658     activate : function(){
28659         this.tabPanel.activate(this.id);
28660     },
28661
28662     /**
28663      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28664      */
28665     disable : function(){
28666         if(this.tabPanel.active != this){
28667             this.disabled = true;
28668             this.pnode.addClass("disabled");
28669         }
28670     },
28671
28672     /**
28673      * Enables this TabPanelItem if it was previously disabled.
28674      */
28675     enable : function(){
28676         this.disabled = false;
28677         this.pnode.removeClass("disabled");
28678     },
28679
28680     /**
28681      * Sets the content for this TabPanelItem.
28682      * @param {String} content The content
28683      * @param {Boolean} loadScripts true to look for and load scripts
28684      */
28685     setContent : function(content, loadScripts){
28686         this.bodyEl.update(content, loadScripts);
28687     },
28688
28689     /**
28690      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28691      * @return {Roo.UpdateManager} The UpdateManager
28692      */
28693     getUpdateManager : function(){
28694         return this.bodyEl.getUpdateManager();
28695     },
28696
28697     /**
28698      * Set a URL to be used to load the content for this TabPanelItem.
28699      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28700      * @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)
28701      * @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)
28702      * @return {Roo.UpdateManager} The UpdateManager
28703      */
28704     setUrl : function(url, params, loadOnce){
28705         if(this.refreshDelegate){
28706             this.un('activate', this.refreshDelegate);
28707         }
28708         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28709         this.on("activate", this.refreshDelegate);
28710         return this.bodyEl.getUpdateManager();
28711     },
28712
28713     /** @private */
28714     _handleRefresh : function(url, params, loadOnce){
28715         if(!loadOnce || !this.loaded){
28716             var updater = this.bodyEl.getUpdateManager();
28717             updater.update(url, params, this._setLoaded.createDelegate(this));
28718         }
28719     },
28720
28721     /**
28722      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28723      *   Will fail silently if the setUrl method has not been called.
28724      *   This does not activate the panel, just updates its content.
28725      */
28726     refresh : function(){
28727         if(this.refreshDelegate){
28728            this.loaded = false;
28729            this.refreshDelegate();
28730         }
28731     },
28732
28733     /** @private */
28734     _setLoaded : function(){
28735         this.loaded = true;
28736     },
28737
28738     /** @private */
28739     closeClick : function(e){
28740         var o = {};
28741         e.stopEvent();
28742         this.fireEvent("beforeclose", this, o);
28743         if(o.cancel !== true){
28744             this.tabPanel.removeTab(this.id);
28745         }
28746     },
28747     /**
28748      * The text displayed in the tooltip for the close icon.
28749      * @type String
28750      */
28751     closeText : "Close this tab"
28752 });
28753
28754 /** @private */
28755 Roo.TabPanel.prototype.createStrip = function(container){
28756     var strip = document.createElement("div");
28757     strip.className = "x-tabs-wrap";
28758     container.appendChild(strip);
28759     return strip;
28760 };
28761 /** @private */
28762 Roo.TabPanel.prototype.createStripList = function(strip){
28763     // div wrapper for retard IE
28764     // returns the "tr" element.
28765     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28766         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28767         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28768     return strip.firstChild.firstChild.firstChild.firstChild;
28769 };
28770 /** @private */
28771 Roo.TabPanel.prototype.createBody = function(container){
28772     var body = document.createElement("div");
28773     Roo.id(body, "tab-body");
28774     Roo.fly(body).addClass("x-tabs-body");
28775     container.appendChild(body);
28776     return body;
28777 };
28778 /** @private */
28779 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28780     var body = Roo.getDom(id);
28781     if(!body){
28782         body = document.createElement("div");
28783         body.id = id;
28784     }
28785     Roo.fly(body).addClass("x-tabs-item-body");
28786     bodyEl.insertBefore(body, bodyEl.firstChild);
28787     return body;
28788 };
28789 /** @private */
28790 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28791     var td = document.createElement("td");
28792     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28793     //stripEl.appendChild(td);
28794     if(closable){
28795         td.className = "x-tabs-closable";
28796         if(!this.closeTpl){
28797             this.closeTpl = new Roo.Template(
28798                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28799                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28800                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28801             );
28802         }
28803         var el = this.closeTpl.overwrite(td, {"text": text});
28804         var close = el.getElementsByTagName("div")[0];
28805         var inner = el.getElementsByTagName("em")[0];
28806         return {"el": el, "close": close, "inner": inner};
28807     } else {
28808         if(!this.tabTpl){
28809             this.tabTpl = new Roo.Template(
28810                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28811                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28812             );
28813         }
28814         var el = this.tabTpl.overwrite(td, {"text": text});
28815         var inner = el.getElementsByTagName("em")[0];
28816         return {"el": el, "inner": inner};
28817     }
28818 };/*
28819  * Based on:
28820  * Ext JS Library 1.1.1
28821  * Copyright(c) 2006-2007, Ext JS, LLC.
28822  *
28823  * Originally Released Under LGPL - original licence link has changed is not relivant.
28824  *
28825  * Fork - LGPL
28826  * <script type="text/javascript">
28827  */
28828
28829 /**
28830  * @class Roo.Button
28831  * @extends Roo.util.Observable
28832  * Simple Button class
28833  * @cfg {String} text The button text
28834  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28835  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28836  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28837  * @cfg {Object} scope The scope of the handler
28838  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28839  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28840  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28841  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28842  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28843  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28844    applies if enableToggle = true)
28845  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28846  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28847   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28848  * @constructor
28849  * Create a new button
28850  * @param {Object} config The config object
28851  */
28852 Roo.Button = function(renderTo, config)
28853 {
28854     if (!config) {
28855         config = renderTo;
28856         renderTo = config.renderTo || false;
28857     }
28858     
28859     Roo.apply(this, config);
28860     this.addEvents({
28861         /**
28862              * @event click
28863              * Fires when this button is clicked
28864              * @param {Button} this
28865              * @param {EventObject} e The click event
28866              */
28867             "click" : true,
28868         /**
28869              * @event toggle
28870              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
28871              * @param {Button} this
28872              * @param {Boolean} pressed
28873              */
28874             "toggle" : true,
28875         /**
28876              * @event mouseover
28877              * Fires when the mouse hovers over the button
28878              * @param {Button} this
28879              * @param {Event} e The event object
28880              */
28881         'mouseover' : true,
28882         /**
28883              * @event mouseout
28884              * Fires when the mouse exits the button
28885              * @param {Button} this
28886              * @param {Event} e The event object
28887              */
28888         'mouseout': true,
28889          /**
28890              * @event render
28891              * Fires when the button is rendered
28892              * @param {Button} this
28893              */
28894         'render': true
28895     });
28896     if(this.menu){
28897         this.menu = Roo.menu.MenuMgr.get(this.menu);
28898     }
28899     // register listeners first!!  - so render can be captured..
28900     Roo.util.Observable.call(this);
28901     if(renderTo){
28902         this.render(renderTo);
28903     }
28904     
28905   
28906 };
28907
28908 Roo.extend(Roo.Button, Roo.util.Observable, {
28909     /**
28910      * 
28911      */
28912     
28913     /**
28914      * Read-only. True if this button is hidden
28915      * @type Boolean
28916      */
28917     hidden : false,
28918     /**
28919      * Read-only. True if this button is disabled
28920      * @type Boolean
28921      */
28922     disabled : false,
28923     /**
28924      * Read-only. True if this button is pressed (only if enableToggle = true)
28925      * @type Boolean
28926      */
28927     pressed : false,
28928
28929     /**
28930      * @cfg {Number} tabIndex 
28931      * The DOM tabIndex for this button (defaults to undefined)
28932      */
28933     tabIndex : undefined,
28934
28935     /**
28936      * @cfg {Boolean} enableToggle
28937      * True to enable pressed/not pressed toggling (defaults to false)
28938      */
28939     enableToggle: false,
28940     /**
28941      * @cfg {Mixed} menu
28942      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
28943      */
28944     menu : undefined,
28945     /**
28946      * @cfg {String} menuAlign
28947      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
28948      */
28949     menuAlign : "tl-bl?",
28950
28951     /**
28952      * @cfg {String} iconCls
28953      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
28954      */
28955     iconCls : undefined,
28956     /**
28957      * @cfg {String} type
28958      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
28959      */
28960     type : 'button',
28961
28962     // private
28963     menuClassTarget: 'tr',
28964
28965     /**
28966      * @cfg {String} clickEvent
28967      * The type of event to map to the button's event handler (defaults to 'click')
28968      */
28969     clickEvent : 'click',
28970
28971     /**
28972      * @cfg {Boolean} handleMouseEvents
28973      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
28974      */
28975     handleMouseEvents : true,
28976
28977     /**
28978      * @cfg {String} tooltipType
28979      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
28980      */
28981     tooltipType : 'qtip',
28982
28983     /**
28984      * @cfg {String} cls
28985      * A CSS class to apply to the button's main element.
28986      */
28987     
28988     /**
28989      * @cfg {Roo.Template} template (Optional)
28990      * An {@link Roo.Template} with which to create the Button's main element. This Template must
28991      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
28992      * require code modifications if required elements (e.g. a button) aren't present.
28993      */
28994
28995     // private
28996     render : function(renderTo){
28997         var btn;
28998         if(this.hideParent){
28999             this.parentEl = Roo.get(renderTo);
29000         }
29001         if(!this.dhconfig){
29002             if(!this.template){
29003                 if(!Roo.Button.buttonTemplate){
29004                     // hideous table template
29005                     Roo.Button.buttonTemplate = new Roo.Template(
29006                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29007                         '<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>',
29008                         "</tr></tbody></table>");
29009                 }
29010                 this.template = Roo.Button.buttonTemplate;
29011             }
29012             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29013             var btnEl = btn.child("button:first");
29014             btnEl.on('focus', this.onFocus, this);
29015             btnEl.on('blur', this.onBlur, this);
29016             if(this.cls){
29017                 btn.addClass(this.cls);
29018             }
29019             if(this.icon){
29020                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29021             }
29022             if(this.iconCls){
29023                 btnEl.addClass(this.iconCls);
29024                 if(!this.cls){
29025                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29026                 }
29027             }
29028             if(this.tabIndex !== undefined){
29029                 btnEl.dom.tabIndex = this.tabIndex;
29030             }
29031             if(this.tooltip){
29032                 if(typeof this.tooltip == 'object'){
29033                     Roo.QuickTips.tips(Roo.apply({
29034                           target: btnEl.id
29035                     }, this.tooltip));
29036                 } else {
29037                     btnEl.dom[this.tooltipType] = this.tooltip;
29038                 }
29039             }
29040         }else{
29041             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29042         }
29043         this.el = btn;
29044         if(this.id){
29045             this.el.dom.id = this.el.id = this.id;
29046         }
29047         if(this.menu){
29048             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29049             this.menu.on("show", this.onMenuShow, this);
29050             this.menu.on("hide", this.onMenuHide, this);
29051         }
29052         btn.addClass("x-btn");
29053         if(Roo.isIE && !Roo.isIE7){
29054             this.autoWidth.defer(1, this);
29055         }else{
29056             this.autoWidth();
29057         }
29058         if(this.handleMouseEvents){
29059             btn.on("mouseover", this.onMouseOver, this);
29060             btn.on("mouseout", this.onMouseOut, this);
29061             btn.on("mousedown", this.onMouseDown, this);
29062         }
29063         btn.on(this.clickEvent, this.onClick, this);
29064         //btn.on("mouseup", this.onMouseUp, this);
29065         if(this.hidden){
29066             this.hide();
29067         }
29068         if(this.disabled){
29069             this.disable();
29070         }
29071         Roo.ButtonToggleMgr.register(this);
29072         if(this.pressed){
29073             this.el.addClass("x-btn-pressed");
29074         }
29075         if(this.repeat){
29076             var repeater = new Roo.util.ClickRepeater(btn,
29077                 typeof this.repeat == "object" ? this.repeat : {}
29078             );
29079             repeater.on("click", this.onClick,  this);
29080         }
29081         
29082         this.fireEvent('render', this);
29083         
29084     },
29085     /**
29086      * Returns the button's underlying element
29087      * @return {Roo.Element} The element
29088      */
29089     getEl : function(){
29090         return this.el;  
29091     },
29092     
29093     /**
29094      * Destroys this Button and removes any listeners.
29095      */
29096     destroy : function(){
29097         Roo.ButtonToggleMgr.unregister(this);
29098         this.el.removeAllListeners();
29099         this.purgeListeners();
29100         this.el.remove();
29101     },
29102
29103     // private
29104     autoWidth : function(){
29105         if(this.el){
29106             this.el.setWidth("auto");
29107             if(Roo.isIE7 && Roo.isStrict){
29108                 var ib = this.el.child('button');
29109                 if(ib && ib.getWidth() > 20){
29110                     ib.clip();
29111                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29112                 }
29113             }
29114             if(this.minWidth){
29115                 if(this.hidden){
29116                     this.el.beginMeasure();
29117                 }
29118                 if(this.el.getWidth() < this.minWidth){
29119                     this.el.setWidth(this.minWidth);
29120                 }
29121                 if(this.hidden){
29122                     this.el.endMeasure();
29123                 }
29124             }
29125         }
29126     },
29127
29128     /**
29129      * Assigns this button's click handler
29130      * @param {Function} handler The function to call when the button is clicked
29131      * @param {Object} scope (optional) Scope for the function passed in
29132      */
29133     setHandler : function(handler, scope){
29134         this.handler = handler;
29135         this.scope = scope;  
29136     },
29137     
29138     /**
29139      * Sets this button's text
29140      * @param {String} text The button text
29141      */
29142     setText : function(text){
29143         this.text = text;
29144         if(this.el){
29145             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29146         }
29147         this.autoWidth();
29148     },
29149     
29150     /**
29151      * Gets the text for this button
29152      * @return {String} The button text
29153      */
29154     getText : function(){
29155         return this.text;  
29156     },
29157     
29158     /**
29159      * Show this button
29160      */
29161     show: function(){
29162         this.hidden = false;
29163         if(this.el){
29164             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29165         }
29166     },
29167     
29168     /**
29169      * Hide this button
29170      */
29171     hide: function(){
29172         this.hidden = true;
29173         if(this.el){
29174             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29175         }
29176     },
29177     
29178     /**
29179      * Convenience function for boolean show/hide
29180      * @param {Boolean} visible True to show, false to hide
29181      */
29182     setVisible: function(visible){
29183         if(visible) {
29184             this.show();
29185         }else{
29186             this.hide();
29187         }
29188     },
29189     
29190     /**
29191      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29192      * @param {Boolean} state (optional) Force a particular state
29193      */
29194     toggle : function(state){
29195         state = state === undefined ? !this.pressed : state;
29196         if(state != this.pressed){
29197             if(state){
29198                 this.el.addClass("x-btn-pressed");
29199                 this.pressed = true;
29200                 this.fireEvent("toggle", this, true);
29201             }else{
29202                 this.el.removeClass("x-btn-pressed");
29203                 this.pressed = false;
29204                 this.fireEvent("toggle", this, false);
29205             }
29206             if(this.toggleHandler){
29207                 this.toggleHandler.call(this.scope || this, this, state);
29208             }
29209         }
29210     },
29211     
29212     /**
29213      * Focus the button
29214      */
29215     focus : function(){
29216         this.el.child('button:first').focus();
29217     },
29218     
29219     /**
29220      * Disable this button
29221      */
29222     disable : function(){
29223         if(this.el){
29224             this.el.addClass("x-btn-disabled");
29225         }
29226         this.disabled = true;
29227     },
29228     
29229     /**
29230      * Enable this button
29231      */
29232     enable : function(){
29233         if(this.el){
29234             this.el.removeClass("x-btn-disabled");
29235         }
29236         this.disabled = false;
29237     },
29238
29239     /**
29240      * Convenience function for boolean enable/disable
29241      * @param {Boolean} enabled True to enable, false to disable
29242      */
29243     setDisabled : function(v){
29244         this[v !== true ? "enable" : "disable"]();
29245     },
29246
29247     // private
29248     onClick : function(e)
29249     {
29250         if(e){
29251             e.preventDefault();
29252         }
29253         if(e.button != 0){
29254             return;
29255         }
29256         if(!this.disabled){
29257             if(this.enableToggle){
29258                 this.toggle();
29259             }
29260             if(this.menu && !this.menu.isVisible()){
29261                 this.menu.show(this.el, this.menuAlign);
29262             }
29263             this.fireEvent("click", this, e);
29264             if(this.handler){
29265                 this.el.removeClass("x-btn-over");
29266                 this.handler.call(this.scope || this, this, e);
29267             }
29268         }
29269     },
29270     // private
29271     onMouseOver : function(e){
29272         if(!this.disabled){
29273             this.el.addClass("x-btn-over");
29274             this.fireEvent('mouseover', this, e);
29275         }
29276     },
29277     // private
29278     onMouseOut : function(e){
29279         if(!e.within(this.el,  true)){
29280             this.el.removeClass("x-btn-over");
29281             this.fireEvent('mouseout', this, e);
29282         }
29283     },
29284     // private
29285     onFocus : function(e){
29286         if(!this.disabled){
29287             this.el.addClass("x-btn-focus");
29288         }
29289     },
29290     // private
29291     onBlur : function(e){
29292         this.el.removeClass("x-btn-focus");
29293     },
29294     // private
29295     onMouseDown : function(e){
29296         if(!this.disabled && e.button == 0){
29297             this.el.addClass("x-btn-click");
29298             Roo.get(document).on('mouseup', this.onMouseUp, this);
29299         }
29300     },
29301     // private
29302     onMouseUp : function(e){
29303         if(e.button == 0){
29304             this.el.removeClass("x-btn-click");
29305             Roo.get(document).un('mouseup', this.onMouseUp, this);
29306         }
29307     },
29308     // private
29309     onMenuShow : function(e){
29310         this.el.addClass("x-btn-menu-active");
29311     },
29312     // private
29313     onMenuHide : function(e){
29314         this.el.removeClass("x-btn-menu-active");
29315     }   
29316 });
29317
29318 // Private utility class used by Button
29319 Roo.ButtonToggleMgr = function(){
29320    var groups = {};
29321    
29322    function toggleGroup(btn, state){
29323        if(state){
29324            var g = groups[btn.toggleGroup];
29325            for(var i = 0, l = g.length; i < l; i++){
29326                if(g[i] != btn){
29327                    g[i].toggle(false);
29328                }
29329            }
29330        }
29331    }
29332    
29333    return {
29334        register : function(btn){
29335            if(!btn.toggleGroup){
29336                return;
29337            }
29338            var g = groups[btn.toggleGroup];
29339            if(!g){
29340                g = groups[btn.toggleGroup] = [];
29341            }
29342            g.push(btn);
29343            btn.on("toggle", toggleGroup);
29344        },
29345        
29346        unregister : function(btn){
29347            if(!btn.toggleGroup){
29348                return;
29349            }
29350            var g = groups[btn.toggleGroup];
29351            if(g){
29352                g.remove(btn);
29353                btn.un("toggle", toggleGroup);
29354            }
29355        }
29356    };
29357 }();/*
29358  * Based on:
29359  * Ext JS Library 1.1.1
29360  * Copyright(c) 2006-2007, Ext JS, LLC.
29361  *
29362  * Originally Released Under LGPL - original licence link has changed is not relivant.
29363  *
29364  * Fork - LGPL
29365  * <script type="text/javascript">
29366  */
29367  
29368 /**
29369  * @class Roo.SplitButton
29370  * @extends Roo.Button
29371  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29372  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29373  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29374  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29375  * @cfg {String} arrowTooltip The title attribute of the arrow
29376  * @constructor
29377  * Create a new menu button
29378  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29379  * @param {Object} config The config object
29380  */
29381 Roo.SplitButton = function(renderTo, config){
29382     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29383     /**
29384      * @event arrowclick
29385      * Fires when this button's arrow is clicked
29386      * @param {SplitButton} this
29387      * @param {EventObject} e The click event
29388      */
29389     this.addEvents({"arrowclick":true});
29390 };
29391
29392 Roo.extend(Roo.SplitButton, Roo.Button, {
29393     render : function(renderTo){
29394         // this is one sweet looking template!
29395         var tpl = new Roo.Template(
29396             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29397             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29398             '<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>',
29399             "</tbody></table></td><td>",
29400             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29401             '<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>',
29402             "</tbody></table></td></tr></table>"
29403         );
29404         var btn = tpl.append(renderTo, [this.text, this.type], true);
29405         var btnEl = btn.child("button");
29406         if(this.cls){
29407             btn.addClass(this.cls);
29408         }
29409         if(this.icon){
29410             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29411         }
29412         if(this.iconCls){
29413             btnEl.addClass(this.iconCls);
29414             if(!this.cls){
29415                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29416             }
29417         }
29418         this.el = btn;
29419         if(this.handleMouseEvents){
29420             btn.on("mouseover", this.onMouseOver, this);
29421             btn.on("mouseout", this.onMouseOut, this);
29422             btn.on("mousedown", this.onMouseDown, this);
29423             btn.on("mouseup", this.onMouseUp, this);
29424         }
29425         btn.on(this.clickEvent, this.onClick, this);
29426         if(this.tooltip){
29427             if(typeof this.tooltip == 'object'){
29428                 Roo.QuickTips.tips(Roo.apply({
29429                       target: btnEl.id
29430                 }, this.tooltip));
29431             } else {
29432                 btnEl.dom[this.tooltipType] = this.tooltip;
29433             }
29434         }
29435         if(this.arrowTooltip){
29436             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29437         }
29438         if(this.hidden){
29439             this.hide();
29440         }
29441         if(this.disabled){
29442             this.disable();
29443         }
29444         if(this.pressed){
29445             this.el.addClass("x-btn-pressed");
29446         }
29447         if(Roo.isIE && !Roo.isIE7){
29448             this.autoWidth.defer(1, this);
29449         }else{
29450             this.autoWidth();
29451         }
29452         if(this.menu){
29453             this.menu.on("show", this.onMenuShow, this);
29454             this.menu.on("hide", this.onMenuHide, this);
29455         }
29456         this.fireEvent('render', this);
29457     },
29458
29459     // private
29460     autoWidth : function(){
29461         if(this.el){
29462             var tbl = this.el.child("table:first");
29463             var tbl2 = this.el.child("table:last");
29464             this.el.setWidth("auto");
29465             tbl.setWidth("auto");
29466             if(Roo.isIE7 && Roo.isStrict){
29467                 var ib = this.el.child('button:first');
29468                 if(ib && ib.getWidth() > 20){
29469                     ib.clip();
29470                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29471                 }
29472             }
29473             if(this.minWidth){
29474                 if(this.hidden){
29475                     this.el.beginMeasure();
29476                 }
29477                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29478                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29479                 }
29480                 if(this.hidden){
29481                     this.el.endMeasure();
29482                 }
29483             }
29484             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29485         } 
29486     },
29487     /**
29488      * Sets this button's click handler
29489      * @param {Function} handler The function to call when the button is clicked
29490      * @param {Object} scope (optional) Scope for the function passed above
29491      */
29492     setHandler : function(handler, scope){
29493         this.handler = handler;
29494         this.scope = scope;  
29495     },
29496     
29497     /**
29498      * Sets this button's arrow click handler
29499      * @param {Function} handler The function to call when the arrow is clicked
29500      * @param {Object} scope (optional) Scope for the function passed above
29501      */
29502     setArrowHandler : function(handler, scope){
29503         this.arrowHandler = handler;
29504         this.scope = scope;  
29505     },
29506     
29507     /**
29508      * Focus the button
29509      */
29510     focus : function(){
29511         if(this.el){
29512             this.el.child("button:first").focus();
29513         }
29514     },
29515
29516     // private
29517     onClick : function(e){
29518         e.preventDefault();
29519         if(!this.disabled){
29520             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29521                 if(this.menu && !this.menu.isVisible()){
29522                     this.menu.show(this.el, this.menuAlign);
29523                 }
29524                 this.fireEvent("arrowclick", this, e);
29525                 if(this.arrowHandler){
29526                     this.arrowHandler.call(this.scope || this, this, e);
29527                 }
29528             }else{
29529                 this.fireEvent("click", this, e);
29530                 if(this.handler){
29531                     this.handler.call(this.scope || this, this, e);
29532                 }
29533             }
29534         }
29535     },
29536     // private
29537     onMouseDown : function(e){
29538         if(!this.disabled){
29539             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29540         }
29541     },
29542     // private
29543     onMouseUp : function(e){
29544         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29545     }   
29546 });
29547
29548
29549 // backwards compat
29550 Roo.MenuButton = Roo.SplitButton;/*
29551  * Based on:
29552  * Ext JS Library 1.1.1
29553  * Copyright(c) 2006-2007, Ext JS, LLC.
29554  *
29555  * Originally Released Under LGPL - original licence link has changed is not relivant.
29556  *
29557  * Fork - LGPL
29558  * <script type="text/javascript">
29559  */
29560
29561 /**
29562  * @class Roo.Toolbar
29563  * Basic Toolbar class.
29564  * @constructor
29565  * Creates a new Toolbar
29566  * @param {Object} container The config object
29567  */ 
29568 Roo.Toolbar = function(container, buttons, config)
29569 {
29570     /// old consturctor format still supported..
29571     if(container instanceof Array){ // omit the container for later rendering
29572         buttons = container;
29573         config = buttons;
29574         container = null;
29575     }
29576     if (typeof(container) == 'object' && container.xtype) {
29577         config = container;
29578         container = config.container;
29579         buttons = config.buttons || []; // not really - use items!!
29580     }
29581     var xitems = [];
29582     if (config && config.items) {
29583         xitems = config.items;
29584         delete config.items;
29585     }
29586     Roo.apply(this, config);
29587     this.buttons = buttons;
29588     
29589     if(container){
29590         this.render(container);
29591     }
29592     this.xitems = xitems;
29593     Roo.each(xitems, function(b) {
29594         this.add(b);
29595     }, this);
29596     
29597 };
29598
29599 Roo.Toolbar.prototype = {
29600     /**
29601      * @cfg {Array} items
29602      * array of button configs or elements to add (will be converted to a MixedCollection)
29603      */
29604     
29605     /**
29606      * @cfg {String/HTMLElement/Element} container
29607      * The id or element that will contain the toolbar
29608      */
29609     // private
29610     render : function(ct){
29611         this.el = Roo.get(ct);
29612         if(this.cls){
29613             this.el.addClass(this.cls);
29614         }
29615         // using a table allows for vertical alignment
29616         // 100% width is needed by Safari...
29617         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29618         this.tr = this.el.child("tr", true);
29619         var autoId = 0;
29620         this.items = new Roo.util.MixedCollection(false, function(o){
29621             return o.id || ("item" + (++autoId));
29622         });
29623         if(this.buttons){
29624             this.add.apply(this, this.buttons);
29625             delete this.buttons;
29626         }
29627     },
29628
29629     /**
29630      * Adds element(s) to the toolbar -- this function takes a variable number of 
29631      * arguments of mixed type and adds them to the toolbar.
29632      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29633      * <ul>
29634      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29635      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29636      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29637      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29638      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29639      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29640      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29641      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29642      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29643      * </ul>
29644      * @param {Mixed} arg2
29645      * @param {Mixed} etc.
29646      */
29647     add : function(){
29648         var a = arguments, l = a.length;
29649         for(var i = 0; i < l; i++){
29650             this._add(a[i]);
29651         }
29652     },
29653     // private..
29654     _add : function(el) {
29655         
29656         if (el.xtype) {
29657             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29658         }
29659         
29660         if (el.applyTo){ // some kind of form field
29661             return this.addField(el);
29662         } 
29663         if (el.render){ // some kind of Toolbar.Item
29664             return this.addItem(el);
29665         }
29666         if (typeof el == "string"){ // string
29667             if(el == "separator" || el == "-"){
29668                 return this.addSeparator();
29669             }
29670             if (el == " "){
29671                 return this.addSpacer();
29672             }
29673             if(el == "->"){
29674                 return this.addFill();
29675             }
29676             return this.addText(el);
29677             
29678         }
29679         if(el.tagName){ // element
29680             return this.addElement(el);
29681         }
29682         if(typeof el == "object"){ // must be button config?
29683             return this.addButton(el);
29684         }
29685         // and now what?!?!
29686         return false;
29687         
29688     },
29689     
29690     /**
29691      * Add an Xtype element
29692      * @param {Object} xtype Xtype Object
29693      * @return {Object} created Object
29694      */
29695     addxtype : function(e){
29696         return this.add(e);  
29697     },
29698     
29699     /**
29700      * Returns the Element for this toolbar.
29701      * @return {Roo.Element}
29702      */
29703     getEl : function(){
29704         return this.el;  
29705     },
29706     
29707     /**
29708      * Adds a separator
29709      * @return {Roo.Toolbar.Item} The separator item
29710      */
29711     addSeparator : function(){
29712         return this.addItem(new Roo.Toolbar.Separator());
29713     },
29714
29715     /**
29716      * Adds a spacer element
29717      * @return {Roo.Toolbar.Spacer} The spacer item
29718      */
29719     addSpacer : function(){
29720         return this.addItem(new Roo.Toolbar.Spacer());
29721     },
29722
29723     /**
29724      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29725      * @return {Roo.Toolbar.Fill} The fill item
29726      */
29727     addFill : function(){
29728         return this.addItem(new Roo.Toolbar.Fill());
29729     },
29730
29731     /**
29732      * Adds any standard HTML element to the toolbar
29733      * @param {String/HTMLElement/Element} el The element or id of the element to add
29734      * @return {Roo.Toolbar.Item} The element's item
29735      */
29736     addElement : function(el){
29737         return this.addItem(new Roo.Toolbar.Item(el));
29738     },
29739     /**
29740      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29741      * @type Roo.util.MixedCollection  
29742      */
29743     items : false,
29744      
29745     /**
29746      * Adds any Toolbar.Item or subclass
29747      * @param {Roo.Toolbar.Item} item
29748      * @return {Roo.Toolbar.Item} The item
29749      */
29750     addItem : function(item){
29751         var td = this.nextBlock();
29752         item.render(td);
29753         this.items.add(item);
29754         return item;
29755     },
29756     
29757     /**
29758      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29759      * @param {Object/Array} config A button config or array of configs
29760      * @return {Roo.Toolbar.Button/Array}
29761      */
29762     addButton : function(config){
29763         if(config instanceof Array){
29764             var buttons = [];
29765             for(var i = 0, len = config.length; i < len; i++) {
29766                 buttons.push(this.addButton(config[i]));
29767             }
29768             return buttons;
29769         }
29770         var b = config;
29771         if(!(config instanceof Roo.Toolbar.Button)){
29772             b = config.split ?
29773                 new Roo.Toolbar.SplitButton(config) :
29774                 new Roo.Toolbar.Button(config);
29775         }
29776         var td = this.nextBlock();
29777         b.render(td);
29778         this.items.add(b);
29779         return b;
29780     },
29781     
29782     /**
29783      * Adds text to the toolbar
29784      * @param {String} text The text to add
29785      * @return {Roo.Toolbar.Item} The element's item
29786      */
29787     addText : function(text){
29788         return this.addItem(new Roo.Toolbar.TextItem(text));
29789     },
29790     
29791     /**
29792      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29793      * @param {Number} index The index where the item is to be inserted
29794      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29795      * @return {Roo.Toolbar.Button/Item}
29796      */
29797     insertButton : function(index, item){
29798         if(item instanceof Array){
29799             var buttons = [];
29800             for(var i = 0, len = item.length; i < len; i++) {
29801                buttons.push(this.insertButton(index + i, item[i]));
29802             }
29803             return buttons;
29804         }
29805         if (!(item instanceof Roo.Toolbar.Button)){
29806            item = new Roo.Toolbar.Button(item);
29807         }
29808         var td = document.createElement("td");
29809         this.tr.insertBefore(td, this.tr.childNodes[index]);
29810         item.render(td);
29811         this.items.insert(index, item);
29812         return item;
29813     },
29814     
29815     /**
29816      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29817      * @param {Object} config
29818      * @return {Roo.Toolbar.Item} The element's item
29819      */
29820     addDom : function(config, returnEl){
29821         var td = this.nextBlock();
29822         Roo.DomHelper.overwrite(td, config);
29823         var ti = new Roo.Toolbar.Item(td.firstChild);
29824         ti.render(td);
29825         this.items.add(ti);
29826         return ti;
29827     },
29828
29829     /**
29830      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29831      * @type Roo.util.MixedCollection  
29832      */
29833     fields : false,
29834     
29835     /**
29836      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29837      * Note: the field should not have been rendered yet. For a field that has already been
29838      * rendered, use {@link #addElement}.
29839      * @param {Roo.form.Field} field
29840      * @return {Roo.ToolbarItem}
29841      */
29842      
29843       
29844     addField : function(field) {
29845         if (!this.fields) {
29846             var autoId = 0;
29847             this.fields = new Roo.util.MixedCollection(false, function(o){
29848                 return o.id || ("item" + (++autoId));
29849             });
29850
29851         }
29852         
29853         var td = this.nextBlock();
29854         field.render(td);
29855         var ti = new Roo.Toolbar.Item(td.firstChild);
29856         ti.render(td);
29857         this.items.add(ti);
29858         this.fields.add(field);
29859         return ti;
29860     },
29861     /**
29862      * Hide the toolbar
29863      * @method hide
29864      */
29865      
29866       
29867     hide : function()
29868     {
29869         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
29870         this.el.child('div').hide();
29871     },
29872     /**
29873      * Show the toolbar
29874      * @method show
29875      */
29876     show : function()
29877     {
29878         this.el.child('div').show();
29879     },
29880       
29881     // private
29882     nextBlock : function(){
29883         var td = document.createElement("td");
29884         this.tr.appendChild(td);
29885         return td;
29886     },
29887
29888     // private
29889     destroy : function(){
29890         if(this.items){ // rendered?
29891             Roo.destroy.apply(Roo, this.items.items);
29892         }
29893         if(this.fields){ // rendered?
29894             Roo.destroy.apply(Roo, this.fields.items);
29895         }
29896         Roo.Element.uncache(this.el, this.tr);
29897     }
29898 };
29899
29900 /**
29901  * @class Roo.Toolbar.Item
29902  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
29903  * @constructor
29904  * Creates a new Item
29905  * @param {HTMLElement} el 
29906  */
29907 Roo.Toolbar.Item = function(el){
29908     var cfg = {};
29909     if (typeof (el.xtype) != 'undefined') {
29910         cfg = el;
29911         el = cfg.el;
29912     }
29913     
29914     this.el = Roo.getDom(el);
29915     this.id = Roo.id(this.el);
29916     this.hidden = false;
29917     
29918     this.addEvents({
29919          /**
29920              * @event render
29921              * Fires when the button is rendered
29922              * @param {Button} this
29923              */
29924         'render': true
29925     });
29926     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
29927 };
29928 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
29929 //Roo.Toolbar.Item.prototype = {
29930     
29931     /**
29932      * Get this item's HTML Element
29933      * @return {HTMLElement}
29934      */
29935     getEl : function(){
29936        return this.el;  
29937     },
29938
29939     // private
29940     render : function(td){
29941         
29942          this.td = td;
29943         td.appendChild(this.el);
29944         
29945         this.fireEvent('render', this);
29946     },
29947     
29948     /**
29949      * Removes and destroys this item.
29950      */
29951     destroy : function(){
29952         this.td.parentNode.removeChild(this.td);
29953     },
29954     
29955     /**
29956      * Shows this item.
29957      */
29958     show: function(){
29959         this.hidden = false;
29960         this.td.style.display = "";
29961     },
29962     
29963     /**
29964      * Hides this item.
29965      */
29966     hide: function(){
29967         this.hidden = true;
29968         this.td.style.display = "none";
29969     },
29970     
29971     /**
29972      * Convenience function for boolean show/hide.
29973      * @param {Boolean} visible true to show/false to hide
29974      */
29975     setVisible: function(visible){
29976         if(visible) {
29977             this.show();
29978         }else{
29979             this.hide();
29980         }
29981     },
29982     
29983     /**
29984      * Try to focus this item.
29985      */
29986     focus : function(){
29987         Roo.fly(this.el).focus();
29988     },
29989     
29990     /**
29991      * Disables this item.
29992      */
29993     disable : function(){
29994         Roo.fly(this.td).addClass("x-item-disabled");
29995         this.disabled = true;
29996         this.el.disabled = true;
29997     },
29998     
29999     /**
30000      * Enables this item.
30001      */
30002     enable : function(){
30003         Roo.fly(this.td).removeClass("x-item-disabled");
30004         this.disabled = false;
30005         this.el.disabled = false;
30006     }
30007 });
30008
30009
30010 /**
30011  * @class Roo.Toolbar.Separator
30012  * @extends Roo.Toolbar.Item
30013  * A simple toolbar separator class
30014  * @constructor
30015  * Creates a new Separator
30016  */
30017 Roo.Toolbar.Separator = function(cfg){
30018     
30019     var s = document.createElement("span");
30020     s.className = "ytb-sep";
30021     if (cfg) {
30022         cfg.el = s;
30023     }
30024     
30025     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30026 };
30027 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30028     enable:Roo.emptyFn,
30029     disable:Roo.emptyFn,
30030     focus:Roo.emptyFn
30031 });
30032
30033 /**
30034  * @class Roo.Toolbar.Spacer
30035  * @extends Roo.Toolbar.Item
30036  * A simple element that adds extra horizontal space to a toolbar.
30037  * @constructor
30038  * Creates a new Spacer
30039  */
30040 Roo.Toolbar.Spacer = function(cfg){
30041     var s = document.createElement("div");
30042     s.className = "ytb-spacer";
30043     if (cfg) {
30044         cfg.el = s;
30045     }
30046     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30047 };
30048 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30049     enable:Roo.emptyFn,
30050     disable:Roo.emptyFn,
30051     focus:Roo.emptyFn
30052 });
30053
30054 /**
30055  * @class Roo.Toolbar.Fill
30056  * @extends Roo.Toolbar.Spacer
30057  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30058  * @constructor
30059  * Creates a new Spacer
30060  */
30061 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30062     // private
30063     render : function(td){
30064         td.style.width = '100%';
30065         Roo.Toolbar.Fill.superclass.render.call(this, td);
30066     }
30067 });
30068
30069 /**
30070  * @class Roo.Toolbar.TextItem
30071  * @extends Roo.Toolbar.Item
30072  * A simple class that renders text directly into a toolbar.
30073  * @constructor
30074  * Creates a new TextItem
30075  * @param {String} text
30076  */
30077 Roo.Toolbar.TextItem = function(cfg){
30078     var  text = cfg || "";
30079     if (typeof(cfg) == 'object') {
30080         text = cfg.text || "";
30081     }  else {
30082         cfg = null;
30083     }
30084     var s = document.createElement("span");
30085     s.className = "ytb-text";
30086     s.innerHTML = text;
30087     if (cfg) {
30088         cfg.el  = s;
30089     }
30090     
30091     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30092 };
30093 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30094     
30095      
30096     enable:Roo.emptyFn,
30097     disable:Roo.emptyFn,
30098     focus:Roo.emptyFn
30099 });
30100
30101 /**
30102  * @class Roo.Toolbar.Button
30103  * @extends Roo.Button
30104  * A button that renders into a toolbar.
30105  * @constructor
30106  * Creates a new Button
30107  * @param {Object} config A standard {@link Roo.Button} config object
30108  */
30109 Roo.Toolbar.Button = function(config){
30110     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30111 };
30112 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30113     render : function(td){
30114         this.td = td;
30115         Roo.Toolbar.Button.superclass.render.call(this, td);
30116     },
30117     
30118     /**
30119      * Removes and destroys this button
30120      */
30121     destroy : function(){
30122         Roo.Toolbar.Button.superclass.destroy.call(this);
30123         this.td.parentNode.removeChild(this.td);
30124     },
30125     
30126     /**
30127      * Shows this button
30128      */
30129     show: function(){
30130         this.hidden = false;
30131         this.td.style.display = "";
30132     },
30133     
30134     /**
30135      * Hides this button
30136      */
30137     hide: function(){
30138         this.hidden = true;
30139         this.td.style.display = "none";
30140     },
30141
30142     /**
30143      * Disables this item
30144      */
30145     disable : function(){
30146         Roo.fly(this.td).addClass("x-item-disabled");
30147         this.disabled = true;
30148     },
30149
30150     /**
30151      * Enables this item
30152      */
30153     enable : function(){
30154         Roo.fly(this.td).removeClass("x-item-disabled");
30155         this.disabled = false;
30156     }
30157 });
30158 // backwards compat
30159 Roo.ToolbarButton = Roo.Toolbar.Button;
30160
30161 /**
30162  * @class Roo.Toolbar.SplitButton
30163  * @extends Roo.SplitButton
30164  * A menu button that renders into a toolbar.
30165  * @constructor
30166  * Creates a new SplitButton
30167  * @param {Object} config A standard {@link Roo.SplitButton} config object
30168  */
30169 Roo.Toolbar.SplitButton = function(config){
30170     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30171 };
30172 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30173     render : function(td){
30174         this.td = td;
30175         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30176     },
30177     
30178     /**
30179      * Removes and destroys this button
30180      */
30181     destroy : function(){
30182         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30183         this.td.parentNode.removeChild(this.td);
30184     },
30185     
30186     /**
30187      * Shows this button
30188      */
30189     show: function(){
30190         this.hidden = false;
30191         this.td.style.display = "";
30192     },
30193     
30194     /**
30195      * Hides this button
30196      */
30197     hide: function(){
30198         this.hidden = true;
30199         this.td.style.display = "none";
30200     }
30201 });
30202
30203 // backwards compat
30204 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30205  * Based on:
30206  * Ext JS Library 1.1.1
30207  * Copyright(c) 2006-2007, Ext JS, LLC.
30208  *
30209  * Originally Released Under LGPL - original licence link has changed is not relivant.
30210  *
30211  * Fork - LGPL
30212  * <script type="text/javascript">
30213  */
30214  
30215 /**
30216  * @class Roo.PagingToolbar
30217  * @extends Roo.Toolbar
30218  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30219  * @constructor
30220  * Create a new PagingToolbar
30221  * @param {Object} config The config object
30222  */
30223 Roo.PagingToolbar = function(el, ds, config)
30224 {
30225     // old args format still supported... - xtype is prefered..
30226     if (typeof(el) == 'object' && el.xtype) {
30227         // created from xtype...
30228         config = el;
30229         ds = el.dataSource;
30230         el = config.container;
30231     }
30232     var items = [];
30233     if (config.items) {
30234         items = config.items;
30235         config.items = [];
30236     }
30237     
30238     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30239     this.ds = ds;
30240     this.cursor = 0;
30241     this.renderButtons(this.el);
30242     this.bind(ds);
30243     
30244     // supprot items array.
30245    
30246     Roo.each(items, function(e) {
30247         this.add(Roo.factory(e));
30248     },this);
30249     
30250 };
30251
30252 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30253     /**
30254      * @cfg {Roo.data.Store} dataSource
30255      * The underlying data store providing the paged data
30256      */
30257     /**
30258      * @cfg {String/HTMLElement/Element} container
30259      * container The id or element that will contain the toolbar
30260      */
30261     /**
30262      * @cfg {Boolean} displayInfo
30263      * True to display the displayMsg (defaults to false)
30264      */
30265     /**
30266      * @cfg {Number} pageSize
30267      * The number of records to display per page (defaults to 20)
30268      */
30269     pageSize: 20,
30270     /**
30271      * @cfg {String} displayMsg
30272      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30273      */
30274     displayMsg : 'Displaying {0} - {1} of {2}',
30275     /**
30276      * @cfg {String} emptyMsg
30277      * The message to display when no records are found (defaults to "No data to display")
30278      */
30279     emptyMsg : 'No data to display',
30280     /**
30281      * Customizable piece of the default paging text (defaults to "Page")
30282      * @type String
30283      */
30284     beforePageText : "Page",
30285     /**
30286      * Customizable piece of the default paging text (defaults to "of %0")
30287      * @type String
30288      */
30289     afterPageText : "of {0}",
30290     /**
30291      * Customizable piece of the default paging text (defaults to "First Page")
30292      * @type String
30293      */
30294     firstText : "First Page",
30295     /**
30296      * Customizable piece of the default paging text (defaults to "Previous Page")
30297      * @type String
30298      */
30299     prevText : "Previous Page",
30300     /**
30301      * Customizable piece of the default paging text (defaults to "Next Page")
30302      * @type String
30303      */
30304     nextText : "Next Page",
30305     /**
30306      * Customizable piece of the default paging text (defaults to "Last Page")
30307      * @type String
30308      */
30309     lastText : "Last Page",
30310     /**
30311      * Customizable piece of the default paging text (defaults to "Refresh")
30312      * @type String
30313      */
30314     refreshText : "Refresh",
30315
30316     // private
30317     renderButtons : function(el){
30318         Roo.PagingToolbar.superclass.render.call(this, el);
30319         this.first = this.addButton({
30320             tooltip: this.firstText,
30321             cls: "x-btn-icon x-grid-page-first",
30322             disabled: true,
30323             handler: this.onClick.createDelegate(this, ["first"])
30324         });
30325         this.prev = this.addButton({
30326             tooltip: this.prevText,
30327             cls: "x-btn-icon x-grid-page-prev",
30328             disabled: true,
30329             handler: this.onClick.createDelegate(this, ["prev"])
30330         });
30331         //this.addSeparator();
30332         this.add(this.beforePageText);
30333         this.field = Roo.get(this.addDom({
30334            tag: "input",
30335            type: "text",
30336            size: "3",
30337            value: "1",
30338            cls: "x-grid-page-number"
30339         }).el);
30340         this.field.on("keydown", this.onPagingKeydown, this);
30341         this.field.on("focus", function(){this.dom.select();});
30342         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30343         this.field.setHeight(18);
30344         //this.addSeparator();
30345         this.next = this.addButton({
30346             tooltip: this.nextText,
30347             cls: "x-btn-icon x-grid-page-next",
30348             disabled: true,
30349             handler: this.onClick.createDelegate(this, ["next"])
30350         });
30351         this.last = this.addButton({
30352             tooltip: this.lastText,
30353             cls: "x-btn-icon x-grid-page-last",
30354             disabled: true,
30355             handler: this.onClick.createDelegate(this, ["last"])
30356         });
30357         //this.addSeparator();
30358         this.loading = this.addButton({
30359             tooltip: this.refreshText,
30360             cls: "x-btn-icon x-grid-loading",
30361             handler: this.onClick.createDelegate(this, ["refresh"])
30362         });
30363
30364         if(this.displayInfo){
30365             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30366         }
30367     },
30368
30369     // private
30370     updateInfo : function(){
30371         if(this.displayEl){
30372             var count = this.ds.getCount();
30373             var msg = count == 0 ?
30374                 this.emptyMsg :
30375                 String.format(
30376                     this.displayMsg,
30377                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30378                 );
30379             this.displayEl.update(msg);
30380         }
30381     },
30382
30383     // private
30384     onLoad : function(ds, r, o){
30385        this.cursor = o.params ? o.params.start : 0;
30386        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30387
30388        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30389        this.field.dom.value = ap;
30390        this.first.setDisabled(ap == 1);
30391        this.prev.setDisabled(ap == 1);
30392        this.next.setDisabled(ap == ps);
30393        this.last.setDisabled(ap == ps);
30394        this.loading.enable();
30395        this.updateInfo();
30396     },
30397
30398     // private
30399     getPageData : function(){
30400         var total = this.ds.getTotalCount();
30401         return {
30402             total : total,
30403             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30404             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30405         };
30406     },
30407
30408     // private
30409     onLoadError : function(){
30410         this.loading.enable();
30411     },
30412
30413     // private
30414     onPagingKeydown : function(e){
30415         var k = e.getKey();
30416         var d = this.getPageData();
30417         if(k == e.RETURN){
30418             var v = this.field.dom.value, pageNum;
30419             if(!v || isNaN(pageNum = parseInt(v, 10))){
30420                 this.field.dom.value = d.activePage;
30421                 return;
30422             }
30423             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30424             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30425             e.stopEvent();
30426         }
30427         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))
30428         {
30429           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30430           this.field.dom.value = pageNum;
30431           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30432           e.stopEvent();
30433         }
30434         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30435         {
30436           var v = this.field.dom.value, pageNum; 
30437           var increment = (e.shiftKey) ? 10 : 1;
30438           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30439             increment *= -1;
30440           }
30441           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30442             this.field.dom.value = d.activePage;
30443             return;
30444           }
30445           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30446           {
30447             this.field.dom.value = parseInt(v, 10) + increment;
30448             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30449             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30450           }
30451           e.stopEvent();
30452         }
30453     },
30454
30455     // private
30456     beforeLoad : function(){
30457         if(this.loading){
30458             this.loading.disable();
30459         }
30460     },
30461
30462     // private
30463     onClick : function(which){
30464         var ds = this.ds;
30465         switch(which){
30466             case "first":
30467                 ds.load({params:{start: 0, limit: this.pageSize}});
30468             break;
30469             case "prev":
30470                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30471             break;
30472             case "next":
30473                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30474             break;
30475             case "last":
30476                 var total = ds.getTotalCount();
30477                 var extra = total % this.pageSize;
30478                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30479                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30480             break;
30481             case "refresh":
30482                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30483             break;
30484         }
30485     },
30486
30487     /**
30488      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30489      * @param {Roo.data.Store} store The data store to unbind
30490      */
30491     unbind : function(ds){
30492         ds.un("beforeload", this.beforeLoad, this);
30493         ds.un("load", this.onLoad, this);
30494         ds.un("loadexception", this.onLoadError, this);
30495         ds.un("remove", this.updateInfo, this);
30496         ds.un("add", this.updateInfo, this);
30497         this.ds = undefined;
30498     },
30499
30500     /**
30501      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30502      * @param {Roo.data.Store} store The data store to bind
30503      */
30504     bind : function(ds){
30505         ds.on("beforeload", this.beforeLoad, this);
30506         ds.on("load", this.onLoad, this);
30507         ds.on("loadexception", this.onLoadError, this);
30508         ds.on("remove", this.updateInfo, this);
30509         ds.on("add", this.updateInfo, this);
30510         this.ds = ds;
30511     }
30512 });/*
30513  * Based on:
30514  * Ext JS Library 1.1.1
30515  * Copyright(c) 2006-2007, Ext JS, LLC.
30516  *
30517  * Originally Released Under LGPL - original licence link has changed is not relivant.
30518  *
30519  * Fork - LGPL
30520  * <script type="text/javascript">
30521  */
30522
30523 /**
30524  * @class Roo.Resizable
30525  * @extends Roo.util.Observable
30526  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30527  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30528  * 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
30529  * the element will be wrapped for you automatically.</p>
30530  * <p>Here is the list of valid resize handles:</p>
30531  * <pre>
30532 Value   Description
30533 ------  -------------------
30534  'n'     north
30535  's'     south
30536  'e'     east
30537  'w'     west
30538  'nw'    northwest
30539  'sw'    southwest
30540  'se'    southeast
30541  'ne'    northeast
30542  'hd'    horizontal drag
30543  'all'   all
30544 </pre>
30545  * <p>Here's an example showing the creation of a typical Resizable:</p>
30546  * <pre><code>
30547 var resizer = new Roo.Resizable("element-id", {
30548     handles: 'all',
30549     minWidth: 200,
30550     minHeight: 100,
30551     maxWidth: 500,
30552     maxHeight: 400,
30553     pinned: true
30554 });
30555 resizer.on("resize", myHandler);
30556 </code></pre>
30557  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30558  * resizer.east.setDisplayed(false);</p>
30559  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30560  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30561  * resize operation's new size (defaults to [0, 0])
30562  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30563  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30564  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30565  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30566  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30567  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30568  * @cfg {Number} width The width of the element in pixels (defaults to null)
30569  * @cfg {Number} height The height of the element in pixels (defaults to null)
30570  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30571  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30572  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30573  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30574  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30575  * in favor of the handles config option (defaults to false)
30576  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30577  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30578  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30579  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30580  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30581  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30582  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30583  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30584  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30585  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30586  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30587  * @constructor
30588  * Create a new resizable component
30589  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30590  * @param {Object} config configuration options
30591   */
30592 Roo.Resizable = function(el, config)
30593 {
30594     this.el = Roo.get(el);
30595
30596     if(config && config.wrap){
30597         config.resizeChild = this.el;
30598         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30599         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30600         this.el.setStyle("overflow", "hidden");
30601         this.el.setPositioning(config.resizeChild.getPositioning());
30602         config.resizeChild.clearPositioning();
30603         if(!config.width || !config.height){
30604             var csize = config.resizeChild.getSize();
30605             this.el.setSize(csize.width, csize.height);
30606         }
30607         if(config.pinned && !config.adjustments){
30608             config.adjustments = "auto";
30609         }
30610     }
30611
30612     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30613     this.proxy.unselectable();
30614     this.proxy.enableDisplayMode('block');
30615
30616     Roo.apply(this, config);
30617
30618     if(this.pinned){
30619         this.disableTrackOver = true;
30620         this.el.addClass("x-resizable-pinned");
30621     }
30622     // if the element isn't positioned, make it relative
30623     var position = this.el.getStyle("position");
30624     if(position != "absolute" && position != "fixed"){
30625         this.el.setStyle("position", "relative");
30626     }
30627     if(!this.handles){ // no handles passed, must be legacy style
30628         this.handles = 's,e,se';
30629         if(this.multiDirectional){
30630             this.handles += ',n,w';
30631         }
30632     }
30633     if(this.handles == "all"){
30634         this.handles = "n s e w ne nw se sw";
30635     }
30636     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30637     var ps = Roo.Resizable.positions;
30638     for(var i = 0, len = hs.length; i < len; i++){
30639         if(hs[i] && ps[hs[i]]){
30640             var pos = ps[hs[i]];
30641             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30642         }
30643     }
30644     // legacy
30645     this.corner = this.southeast;
30646     
30647     // updateBox = the box can move..
30648     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30649         this.updateBox = true;
30650     }
30651
30652     this.activeHandle = null;
30653
30654     if(this.resizeChild){
30655         if(typeof this.resizeChild == "boolean"){
30656             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30657         }else{
30658             this.resizeChild = Roo.get(this.resizeChild, true);
30659         }
30660     }
30661     
30662     if(this.adjustments == "auto"){
30663         var rc = this.resizeChild;
30664         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30665         if(rc && (hw || hn)){
30666             rc.position("relative");
30667             rc.setLeft(hw ? hw.el.getWidth() : 0);
30668             rc.setTop(hn ? hn.el.getHeight() : 0);
30669         }
30670         this.adjustments = [
30671             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30672             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30673         ];
30674     }
30675
30676     if(this.draggable){
30677         this.dd = this.dynamic ?
30678             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30679         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30680     }
30681
30682     // public events
30683     this.addEvents({
30684         /**
30685          * @event beforeresize
30686          * Fired before resize is allowed. Set enabled to false to cancel resize.
30687          * @param {Roo.Resizable} this
30688          * @param {Roo.EventObject} e The mousedown event
30689          */
30690         "beforeresize" : true,
30691         /**
30692          * @event resizing
30693          * Fired a resizing.
30694          * @param {Roo.Resizable} this
30695          * @param {Number} x The new x position
30696          * @param {Number} y The new y position
30697          * @param {Number} w The new w width
30698          * @param {Number} h The new h hight
30699          * @param {Roo.EventObject} e The mouseup event
30700          */
30701         "resizing" : true,
30702         /**
30703          * @event resize
30704          * Fired after a resize.
30705          * @param {Roo.Resizable} this
30706          * @param {Number} width The new width
30707          * @param {Number} height The new height
30708          * @param {Roo.EventObject} e The mouseup event
30709          */
30710         "resize" : true
30711     });
30712
30713     if(this.width !== null && this.height !== null){
30714         this.resizeTo(this.width, this.height);
30715     }else{
30716         this.updateChildSize();
30717     }
30718     if(Roo.isIE){
30719         this.el.dom.style.zoom = 1;
30720     }
30721     Roo.Resizable.superclass.constructor.call(this);
30722 };
30723
30724 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30725         resizeChild : false,
30726         adjustments : [0, 0],
30727         minWidth : 5,
30728         minHeight : 5,
30729         maxWidth : 10000,
30730         maxHeight : 10000,
30731         enabled : true,
30732         animate : false,
30733         duration : .35,
30734         dynamic : false,
30735         handles : false,
30736         multiDirectional : false,
30737         disableTrackOver : false,
30738         easing : 'easeOutStrong',
30739         widthIncrement : 0,
30740         heightIncrement : 0,
30741         pinned : false,
30742         width : null,
30743         height : null,
30744         preserveRatio : false,
30745         transparent: false,
30746         minX: 0,
30747         minY: 0,
30748         draggable: false,
30749
30750         /**
30751          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30752          */
30753         constrainTo: undefined,
30754         /**
30755          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30756          */
30757         resizeRegion: undefined,
30758
30759
30760     /**
30761      * Perform a manual resize
30762      * @param {Number} width
30763      * @param {Number} height
30764      */
30765     resizeTo : function(width, height){
30766         this.el.setSize(width, height);
30767         this.updateChildSize();
30768         this.fireEvent("resize", this, width, height, null);
30769     },
30770
30771     // private
30772     startSizing : function(e, handle){
30773         this.fireEvent("beforeresize", this, e);
30774         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30775
30776             if(!this.overlay){
30777                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30778                 this.overlay.unselectable();
30779                 this.overlay.enableDisplayMode("block");
30780                 this.overlay.on("mousemove", this.onMouseMove, this);
30781                 this.overlay.on("mouseup", this.onMouseUp, this);
30782             }
30783             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30784
30785             this.resizing = true;
30786             this.startBox = this.el.getBox();
30787             this.startPoint = e.getXY();
30788             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30789                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30790
30791             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30792             this.overlay.show();
30793
30794             if(this.constrainTo) {
30795                 var ct = Roo.get(this.constrainTo);
30796                 this.resizeRegion = ct.getRegion().adjust(
30797                     ct.getFrameWidth('t'),
30798                     ct.getFrameWidth('l'),
30799                     -ct.getFrameWidth('b'),
30800                     -ct.getFrameWidth('r')
30801                 );
30802             }
30803
30804             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30805             this.proxy.show();
30806             this.proxy.setBox(this.startBox);
30807             if(!this.dynamic){
30808                 this.proxy.setStyle('visibility', 'visible');
30809             }
30810         }
30811     },
30812
30813     // private
30814     onMouseDown : function(handle, e){
30815         if(this.enabled){
30816             e.stopEvent();
30817             this.activeHandle = handle;
30818             this.startSizing(e, handle);
30819         }
30820     },
30821
30822     // private
30823     onMouseUp : function(e){
30824         var size = this.resizeElement();
30825         this.resizing = false;
30826         this.handleOut();
30827         this.overlay.hide();
30828         this.proxy.hide();
30829         this.fireEvent("resize", this, size.width, size.height, e);
30830     },
30831
30832     // private
30833     updateChildSize : function(){
30834         
30835         if(this.resizeChild){
30836             var el = this.el;
30837             var child = this.resizeChild;
30838             var adj = this.adjustments;
30839             if(el.dom.offsetWidth){
30840                 var b = el.getSize(true);
30841                 child.setSize(b.width+adj[0], b.height+adj[1]);
30842             }
30843             // Second call here for IE
30844             // The first call enables instant resizing and
30845             // the second call corrects scroll bars if they
30846             // exist
30847             if(Roo.isIE){
30848                 setTimeout(function(){
30849                     if(el.dom.offsetWidth){
30850                         var b = el.getSize(true);
30851                         child.setSize(b.width+adj[0], b.height+adj[1]);
30852                     }
30853                 }, 10);
30854             }
30855         }
30856     },
30857
30858     // private
30859     snap : function(value, inc, min){
30860         if(!inc || !value) {
30861             return value;
30862         }
30863         var newValue = value;
30864         var m = value % inc;
30865         if(m > 0){
30866             if(m > (inc/2)){
30867                 newValue = value + (inc-m);
30868             }else{
30869                 newValue = value - m;
30870             }
30871         }
30872         return Math.max(min, newValue);
30873     },
30874
30875     // private
30876     resizeElement : function(){
30877         var box = this.proxy.getBox();
30878         if(this.updateBox){
30879             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
30880         }else{
30881             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
30882         }
30883         this.updateChildSize();
30884         if(!this.dynamic){
30885             this.proxy.hide();
30886         }
30887         return box;
30888     },
30889
30890     // private
30891     constrain : function(v, diff, m, mx){
30892         if(v - diff < m){
30893             diff = v - m;
30894         }else if(v - diff > mx){
30895             diff = mx - v;
30896         }
30897         return diff;
30898     },
30899
30900     // private
30901     onMouseMove : function(e){
30902         
30903         if(this.enabled){
30904             try{// try catch so if something goes wrong the user doesn't get hung
30905
30906             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
30907                 return;
30908             }
30909
30910             //var curXY = this.startPoint;
30911             var curSize = this.curSize || this.startBox;
30912             var x = this.startBox.x, y = this.startBox.y;
30913             var ox = x, oy = y;
30914             var w = curSize.width, h = curSize.height;
30915             var ow = w, oh = h;
30916             var mw = this.minWidth, mh = this.minHeight;
30917             var mxw = this.maxWidth, mxh = this.maxHeight;
30918             var wi = this.widthIncrement;
30919             var hi = this.heightIncrement;
30920
30921             var eventXY = e.getXY();
30922             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
30923             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
30924
30925             var pos = this.activeHandle.position;
30926
30927             switch(pos){
30928                 case "east":
30929                     w += diffX;
30930                     w = Math.min(Math.max(mw, w), mxw);
30931                     break;
30932              
30933                 case "south":
30934                     h += diffY;
30935                     h = Math.min(Math.max(mh, h), mxh);
30936                     break;
30937                 case "southeast":
30938                     w += diffX;
30939                     h += diffY;
30940                     w = Math.min(Math.max(mw, w), mxw);
30941                     h = Math.min(Math.max(mh, h), mxh);
30942                     break;
30943                 case "north":
30944                     diffY = this.constrain(h, diffY, mh, mxh);
30945                     y += diffY;
30946                     h -= diffY;
30947                     break;
30948                 case "hdrag":
30949                     
30950                     if (wi) {
30951                         var adiffX = Math.abs(diffX);
30952                         var sub = (adiffX % wi); // how much 
30953                         if (sub > (wi/2)) { // far enough to snap
30954                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
30955                         } else {
30956                             // remove difference.. 
30957                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
30958                         }
30959                     }
30960                     x += diffX;
30961                     x = Math.max(this.minX, x);
30962                     break;
30963                 case "west":
30964                     diffX = this.constrain(w, diffX, mw, mxw);
30965                     x += diffX;
30966                     w -= diffX;
30967                     break;
30968                 case "northeast":
30969                     w += diffX;
30970                     w = Math.min(Math.max(mw, w), mxw);
30971                     diffY = this.constrain(h, diffY, mh, mxh);
30972                     y += diffY;
30973                     h -= diffY;
30974                     break;
30975                 case "northwest":
30976                     diffX = this.constrain(w, diffX, mw, mxw);
30977                     diffY = this.constrain(h, diffY, mh, mxh);
30978                     y += diffY;
30979                     h -= diffY;
30980                     x += diffX;
30981                     w -= diffX;
30982                     break;
30983                case "southwest":
30984                     diffX = this.constrain(w, diffX, mw, mxw);
30985                     h += diffY;
30986                     h = Math.min(Math.max(mh, h), mxh);
30987                     x += diffX;
30988                     w -= diffX;
30989                     break;
30990             }
30991
30992             var sw = this.snap(w, wi, mw);
30993             var sh = this.snap(h, hi, mh);
30994             if(sw != w || sh != h){
30995                 switch(pos){
30996                     case "northeast":
30997                         y -= sh - h;
30998                     break;
30999                     case "north":
31000                         y -= sh - h;
31001                         break;
31002                     case "southwest":
31003                         x -= sw - w;
31004                     break;
31005                     case "west":
31006                         x -= sw - w;
31007                         break;
31008                     case "northwest":
31009                         x -= sw - w;
31010                         y -= sh - h;
31011                     break;
31012                 }
31013                 w = sw;
31014                 h = sh;
31015             }
31016
31017             if(this.preserveRatio){
31018                 switch(pos){
31019                     case "southeast":
31020                     case "east":
31021                         h = oh * (w/ow);
31022                         h = Math.min(Math.max(mh, h), mxh);
31023                         w = ow * (h/oh);
31024                        break;
31025                     case "south":
31026                         w = ow * (h/oh);
31027                         w = Math.min(Math.max(mw, w), mxw);
31028                         h = oh * (w/ow);
31029                         break;
31030                     case "northeast":
31031                         w = ow * (h/oh);
31032                         w = Math.min(Math.max(mw, w), mxw);
31033                         h = oh * (w/ow);
31034                     break;
31035                     case "north":
31036                         var tw = w;
31037                         w = ow * (h/oh);
31038                         w = Math.min(Math.max(mw, w), mxw);
31039                         h = oh * (w/ow);
31040                         x += (tw - w) / 2;
31041                         break;
31042                     case "southwest":
31043                         h = oh * (w/ow);
31044                         h = Math.min(Math.max(mh, h), mxh);
31045                         var tw = w;
31046                         w = ow * (h/oh);
31047                         x += tw - w;
31048                         break;
31049                     case "west":
31050                         var th = h;
31051                         h = oh * (w/ow);
31052                         h = Math.min(Math.max(mh, h), mxh);
31053                         y += (th - h) / 2;
31054                         var tw = w;
31055                         w = ow * (h/oh);
31056                         x += tw - w;
31057                        break;
31058                     case "northwest":
31059                         var tw = w;
31060                         var th = h;
31061                         h = oh * (w/ow);
31062                         h = Math.min(Math.max(mh, h), mxh);
31063                         w = ow * (h/oh);
31064                         y += th - h;
31065                         x += tw - w;
31066                        break;
31067
31068                 }
31069             }
31070             if (pos == 'hdrag') {
31071                 w = ow;
31072             }
31073             this.proxy.setBounds(x, y, w, h);
31074             if(this.dynamic){
31075                 this.resizeElement();
31076             }
31077             }catch(e){}
31078         }
31079         this.fireEvent("resizing", this, x, y, w, h, e);
31080     },
31081
31082     // private
31083     handleOver : function(){
31084         if(this.enabled){
31085             this.el.addClass("x-resizable-over");
31086         }
31087     },
31088
31089     // private
31090     handleOut : function(){
31091         if(!this.resizing){
31092             this.el.removeClass("x-resizable-over");
31093         }
31094     },
31095
31096     /**
31097      * Returns the element this component is bound to.
31098      * @return {Roo.Element}
31099      */
31100     getEl : function(){
31101         return this.el;
31102     },
31103
31104     /**
31105      * Returns the resizeChild element (or null).
31106      * @return {Roo.Element}
31107      */
31108     getResizeChild : function(){
31109         return this.resizeChild;
31110     },
31111     groupHandler : function()
31112     {
31113         
31114     },
31115     /**
31116      * Destroys this resizable. If the element was wrapped and
31117      * removeEl is not true then the element remains.
31118      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31119      */
31120     destroy : function(removeEl){
31121         this.proxy.remove();
31122         if(this.overlay){
31123             this.overlay.removeAllListeners();
31124             this.overlay.remove();
31125         }
31126         var ps = Roo.Resizable.positions;
31127         for(var k in ps){
31128             if(typeof ps[k] != "function" && this[ps[k]]){
31129                 var h = this[ps[k]];
31130                 h.el.removeAllListeners();
31131                 h.el.remove();
31132             }
31133         }
31134         if(removeEl){
31135             this.el.update("");
31136             this.el.remove();
31137         }
31138     }
31139 });
31140
31141 // private
31142 // hash to map config positions to true positions
31143 Roo.Resizable.positions = {
31144     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31145     hd: "hdrag"
31146 };
31147
31148 // private
31149 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31150     if(!this.tpl){
31151         // only initialize the template if resizable is used
31152         var tpl = Roo.DomHelper.createTemplate(
31153             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31154         );
31155         tpl.compile();
31156         Roo.Resizable.Handle.prototype.tpl = tpl;
31157     }
31158     this.position = pos;
31159     this.rz = rz;
31160     // show north drag fro topdra
31161     var handlepos = pos == 'hdrag' ? 'north' : pos;
31162     
31163     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31164     if (pos == 'hdrag') {
31165         this.el.setStyle('cursor', 'pointer');
31166     }
31167     this.el.unselectable();
31168     if(transparent){
31169         this.el.setOpacity(0);
31170     }
31171     this.el.on("mousedown", this.onMouseDown, this);
31172     if(!disableTrackOver){
31173         this.el.on("mouseover", this.onMouseOver, this);
31174         this.el.on("mouseout", this.onMouseOut, this);
31175     }
31176 };
31177
31178 // private
31179 Roo.Resizable.Handle.prototype = {
31180     afterResize : function(rz){
31181         Roo.log('after?');
31182         // do nothing
31183     },
31184     // private
31185     onMouseDown : function(e){
31186         this.rz.onMouseDown(this, e);
31187     },
31188     // private
31189     onMouseOver : function(e){
31190         this.rz.handleOver(this, e);
31191     },
31192     // private
31193     onMouseOut : function(e){
31194         this.rz.handleOut(this, e);
31195     }
31196 };/*
31197  * Based on:
31198  * Ext JS Library 1.1.1
31199  * Copyright(c) 2006-2007, Ext JS, LLC.
31200  *
31201  * Originally Released Under LGPL - original licence link has changed is not relivant.
31202  *
31203  * Fork - LGPL
31204  * <script type="text/javascript">
31205  */
31206
31207 /**
31208  * @class Roo.Editor
31209  * @extends Roo.Component
31210  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31211  * @constructor
31212  * Create a new Editor
31213  * @param {Roo.form.Field} field The Field object (or descendant)
31214  * @param {Object} config The config object
31215  */
31216 Roo.Editor = function(field, config){
31217     Roo.Editor.superclass.constructor.call(this, config);
31218     this.field = field;
31219     this.addEvents({
31220         /**
31221              * @event beforestartedit
31222              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31223              * false from the handler of this event.
31224              * @param {Editor} this
31225              * @param {Roo.Element} boundEl The underlying element bound to this editor
31226              * @param {Mixed} value The field value being set
31227              */
31228         "beforestartedit" : true,
31229         /**
31230              * @event startedit
31231              * Fires when this editor is displayed
31232              * @param {Roo.Element} boundEl The underlying element bound to this editor
31233              * @param {Mixed} value The starting field value
31234              */
31235         "startedit" : true,
31236         /**
31237              * @event beforecomplete
31238              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31239              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31240              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31241              * event will not fire since no edit actually occurred.
31242              * @param {Editor} this
31243              * @param {Mixed} value The current field value
31244              * @param {Mixed} startValue The original field value
31245              */
31246         "beforecomplete" : true,
31247         /**
31248              * @event complete
31249              * Fires after editing is complete and any changed value has been written to the underlying field.
31250              * @param {Editor} this
31251              * @param {Mixed} value The current field value
31252              * @param {Mixed} startValue The original field value
31253              */
31254         "complete" : true,
31255         /**
31256          * @event specialkey
31257          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31258          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31259          * @param {Roo.form.Field} this
31260          * @param {Roo.EventObject} e The event object
31261          */
31262         "specialkey" : true
31263     });
31264 };
31265
31266 Roo.extend(Roo.Editor, Roo.Component, {
31267     /**
31268      * @cfg {Boolean/String} autosize
31269      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31270      * or "height" to adopt the height only (defaults to false)
31271      */
31272     /**
31273      * @cfg {Boolean} revertInvalid
31274      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31275      * validation fails (defaults to true)
31276      */
31277     /**
31278      * @cfg {Boolean} ignoreNoChange
31279      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31280      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31281      * will never be ignored.
31282      */
31283     /**
31284      * @cfg {Boolean} hideEl
31285      * False to keep the bound element visible while the editor is displayed (defaults to true)
31286      */
31287     /**
31288      * @cfg {Mixed} value
31289      * The data value of the underlying field (defaults to "")
31290      */
31291     value : "",
31292     /**
31293      * @cfg {String} alignment
31294      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31295      */
31296     alignment: "c-c?",
31297     /**
31298      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31299      * for bottom-right shadow (defaults to "frame")
31300      */
31301     shadow : "frame",
31302     /**
31303      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31304      */
31305     constrain : false,
31306     /**
31307      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31308      */
31309     completeOnEnter : false,
31310     /**
31311      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31312      */
31313     cancelOnEsc : false,
31314     /**
31315      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31316      */
31317     updateEl : false,
31318
31319     // private
31320     onRender : function(ct, position){
31321         this.el = new Roo.Layer({
31322             shadow: this.shadow,
31323             cls: "x-editor",
31324             parentEl : ct,
31325             shim : this.shim,
31326             shadowOffset:4,
31327             id: this.id,
31328             constrain: this.constrain
31329         });
31330         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31331         if(this.field.msgTarget != 'title'){
31332             this.field.msgTarget = 'qtip';
31333         }
31334         this.field.render(this.el);
31335         if(Roo.isGecko){
31336             this.field.el.dom.setAttribute('autocomplete', 'off');
31337         }
31338         this.field.on("specialkey", this.onSpecialKey, this);
31339         if(this.swallowKeys){
31340             this.field.el.swallowEvent(['keydown','keypress']);
31341         }
31342         this.field.show();
31343         this.field.on("blur", this.onBlur, this);
31344         if(this.field.grow){
31345             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31346         }
31347     },
31348
31349     onSpecialKey : function(field, e)
31350     {
31351         //Roo.log('editor onSpecialKey');
31352         if(this.completeOnEnter && e.getKey() == e.ENTER){
31353             e.stopEvent();
31354             this.completeEdit();
31355             return;
31356         }
31357         // do not fire special key otherwise it might hide close the editor...
31358         if(e.getKey() == e.ENTER){    
31359             return;
31360         }
31361         if(this.cancelOnEsc && e.getKey() == e.ESC){
31362             this.cancelEdit();
31363             return;
31364         } 
31365         this.fireEvent('specialkey', field, e);
31366     
31367     },
31368
31369     /**
31370      * Starts the editing process and shows the editor.
31371      * @param {String/HTMLElement/Element} el The element to edit
31372      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31373       * to the innerHTML of el.
31374      */
31375     startEdit : function(el, value){
31376         if(this.editing){
31377             this.completeEdit();
31378         }
31379         this.boundEl = Roo.get(el);
31380         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31381         if(!this.rendered){
31382             this.render(this.parentEl || document.body);
31383         }
31384         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31385             return;
31386         }
31387         this.startValue = v;
31388         this.field.setValue(v);
31389         if(this.autoSize){
31390             var sz = this.boundEl.getSize();
31391             switch(this.autoSize){
31392                 case "width":
31393                 this.setSize(sz.width,  "");
31394                 break;
31395                 case "height":
31396                 this.setSize("",  sz.height);
31397                 break;
31398                 default:
31399                 this.setSize(sz.width,  sz.height);
31400             }
31401         }
31402         this.el.alignTo(this.boundEl, this.alignment);
31403         this.editing = true;
31404         if(Roo.QuickTips){
31405             Roo.QuickTips.disable();
31406         }
31407         this.show();
31408     },
31409
31410     /**
31411      * Sets the height and width of this editor.
31412      * @param {Number} width The new width
31413      * @param {Number} height The new height
31414      */
31415     setSize : function(w, h){
31416         this.field.setSize(w, h);
31417         if(this.el){
31418             this.el.sync();
31419         }
31420     },
31421
31422     /**
31423      * Realigns the editor to the bound field based on the current alignment config value.
31424      */
31425     realign : function(){
31426         this.el.alignTo(this.boundEl, this.alignment);
31427     },
31428
31429     /**
31430      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31431      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31432      */
31433     completeEdit : function(remainVisible){
31434         if(!this.editing){
31435             return;
31436         }
31437         var v = this.getValue();
31438         if(this.revertInvalid !== false && !this.field.isValid()){
31439             v = this.startValue;
31440             this.cancelEdit(true);
31441         }
31442         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31443             this.editing = false;
31444             this.hide();
31445             return;
31446         }
31447         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31448             this.editing = false;
31449             if(this.updateEl && this.boundEl){
31450                 this.boundEl.update(v);
31451             }
31452             if(remainVisible !== true){
31453                 this.hide();
31454             }
31455             this.fireEvent("complete", this, v, this.startValue);
31456         }
31457     },
31458
31459     // private
31460     onShow : function(){
31461         this.el.show();
31462         if(this.hideEl !== false){
31463             this.boundEl.hide();
31464         }
31465         this.field.show();
31466         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31467             this.fixIEFocus = true;
31468             this.deferredFocus.defer(50, this);
31469         }else{
31470             this.field.focus();
31471         }
31472         this.fireEvent("startedit", this.boundEl, this.startValue);
31473     },
31474
31475     deferredFocus : function(){
31476         if(this.editing){
31477             this.field.focus();
31478         }
31479     },
31480
31481     /**
31482      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31483      * reverted to the original starting value.
31484      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31485      * cancel (defaults to false)
31486      */
31487     cancelEdit : function(remainVisible){
31488         if(this.editing){
31489             this.setValue(this.startValue);
31490             if(remainVisible !== true){
31491                 this.hide();
31492             }
31493         }
31494     },
31495
31496     // private
31497     onBlur : function(){
31498         if(this.allowBlur !== true && this.editing){
31499             this.completeEdit();
31500         }
31501     },
31502
31503     // private
31504     onHide : function(){
31505         if(this.editing){
31506             this.completeEdit();
31507             return;
31508         }
31509         this.field.blur();
31510         if(this.field.collapse){
31511             this.field.collapse();
31512         }
31513         this.el.hide();
31514         if(this.hideEl !== false){
31515             this.boundEl.show();
31516         }
31517         if(Roo.QuickTips){
31518             Roo.QuickTips.enable();
31519         }
31520     },
31521
31522     /**
31523      * Sets the data value of the editor
31524      * @param {Mixed} value Any valid value supported by the underlying field
31525      */
31526     setValue : function(v){
31527         this.field.setValue(v);
31528     },
31529
31530     /**
31531      * Gets the data value of the editor
31532      * @return {Mixed} The data value
31533      */
31534     getValue : function(){
31535         return this.field.getValue();
31536     }
31537 });/*
31538  * Based on:
31539  * Ext JS Library 1.1.1
31540  * Copyright(c) 2006-2007, Ext JS, LLC.
31541  *
31542  * Originally Released Under LGPL - original licence link has changed is not relivant.
31543  *
31544  * Fork - LGPL
31545  * <script type="text/javascript">
31546  */
31547  
31548 /**
31549  * @class Roo.BasicDialog
31550  * @extends Roo.util.Observable
31551  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31552  * <pre><code>
31553 var dlg = new Roo.BasicDialog("my-dlg", {
31554     height: 200,
31555     width: 300,
31556     minHeight: 100,
31557     minWidth: 150,
31558     modal: true,
31559     proxyDrag: true,
31560     shadow: true
31561 });
31562 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31563 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31564 dlg.addButton('Cancel', dlg.hide, dlg);
31565 dlg.show();
31566 </code></pre>
31567   <b>A Dialog should always be a direct child of the body element.</b>
31568  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31569  * @cfg {String} title Default text to display in the title bar (defaults to null)
31570  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31571  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31572  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31573  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31574  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31575  * (defaults to null with no animation)
31576  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31577  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31578  * property for valid values (defaults to 'all')
31579  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31580  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31581  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31582  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31583  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31584  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31585  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31586  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31587  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31588  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31589  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31590  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31591  * draggable = true (defaults to false)
31592  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31593  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31594  * shadow (defaults to false)
31595  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31596  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31597  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31598  * @cfg {Array} buttons Array of buttons
31599  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31600  * @constructor
31601  * Create a new BasicDialog.
31602  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31603  * @param {Object} config Configuration options
31604  */
31605 Roo.BasicDialog = function(el, config){
31606     this.el = Roo.get(el);
31607     var dh = Roo.DomHelper;
31608     if(!this.el && config && config.autoCreate){
31609         if(typeof config.autoCreate == "object"){
31610             if(!config.autoCreate.id){
31611                 config.autoCreate.id = el;
31612             }
31613             this.el = dh.append(document.body,
31614                         config.autoCreate, true);
31615         }else{
31616             this.el = dh.append(document.body,
31617                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31618         }
31619     }
31620     el = this.el;
31621     el.setDisplayed(true);
31622     el.hide = this.hideAction;
31623     this.id = el.id;
31624     el.addClass("x-dlg");
31625
31626     Roo.apply(this, config);
31627
31628     this.proxy = el.createProxy("x-dlg-proxy");
31629     this.proxy.hide = this.hideAction;
31630     this.proxy.setOpacity(.5);
31631     this.proxy.hide();
31632
31633     if(config.width){
31634         el.setWidth(config.width);
31635     }
31636     if(config.height){
31637         el.setHeight(config.height);
31638     }
31639     this.size = el.getSize();
31640     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31641         this.xy = [config.x,config.y];
31642     }else{
31643         this.xy = el.getCenterXY(true);
31644     }
31645     /** The header element @type Roo.Element */
31646     this.header = el.child("> .x-dlg-hd");
31647     /** The body element @type Roo.Element */
31648     this.body = el.child("> .x-dlg-bd");
31649     /** The footer element @type Roo.Element */
31650     this.footer = el.child("> .x-dlg-ft");
31651
31652     if(!this.header){
31653         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31654     }
31655     if(!this.body){
31656         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31657     }
31658
31659     this.header.unselectable();
31660     if(this.title){
31661         this.header.update(this.title);
31662     }
31663     // this element allows the dialog to be focused for keyboard event
31664     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31665     this.focusEl.swallowEvent("click", true);
31666
31667     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31668
31669     // wrap the body and footer for special rendering
31670     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31671     if(this.footer){
31672         this.bwrap.dom.appendChild(this.footer.dom);
31673     }
31674
31675     this.bg = this.el.createChild({
31676         tag: "div", cls:"x-dlg-bg",
31677         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31678     });
31679     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31680
31681
31682     if(this.autoScroll !== false && !this.autoTabs){
31683         this.body.setStyle("overflow", "auto");
31684     }
31685
31686     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31687
31688     if(this.closable !== false){
31689         this.el.addClass("x-dlg-closable");
31690         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31691         this.close.on("click", this.closeClick, this);
31692         this.close.addClassOnOver("x-dlg-close-over");
31693     }
31694     if(this.collapsible !== false){
31695         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31696         this.collapseBtn.on("click", this.collapseClick, this);
31697         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31698         this.header.on("dblclick", this.collapseClick, this);
31699     }
31700     if(this.resizable !== false){
31701         this.el.addClass("x-dlg-resizable");
31702         this.resizer = new Roo.Resizable(el, {
31703             minWidth: this.minWidth || 80,
31704             minHeight:this.minHeight || 80,
31705             handles: this.resizeHandles || "all",
31706             pinned: true
31707         });
31708         this.resizer.on("beforeresize", this.beforeResize, this);
31709         this.resizer.on("resize", this.onResize, this);
31710     }
31711     if(this.draggable !== false){
31712         el.addClass("x-dlg-draggable");
31713         if (!this.proxyDrag) {
31714             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31715         }
31716         else {
31717             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31718         }
31719         dd.setHandleElId(this.header.id);
31720         dd.endDrag = this.endMove.createDelegate(this);
31721         dd.startDrag = this.startMove.createDelegate(this);
31722         dd.onDrag = this.onDrag.createDelegate(this);
31723         dd.scroll = false;
31724         this.dd = dd;
31725     }
31726     if(this.modal){
31727         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31728         this.mask.enableDisplayMode("block");
31729         this.mask.hide();
31730         this.el.addClass("x-dlg-modal");
31731     }
31732     if(this.shadow){
31733         this.shadow = new Roo.Shadow({
31734             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31735             offset : this.shadowOffset
31736         });
31737     }else{
31738         this.shadowOffset = 0;
31739     }
31740     if(Roo.useShims && this.shim !== false){
31741         this.shim = this.el.createShim();
31742         this.shim.hide = this.hideAction;
31743         this.shim.hide();
31744     }else{
31745         this.shim = false;
31746     }
31747     if(this.autoTabs){
31748         this.initTabs();
31749     }
31750     if (this.buttons) { 
31751         var bts= this.buttons;
31752         this.buttons = [];
31753         Roo.each(bts, function(b) {
31754             this.addButton(b);
31755         }, this);
31756     }
31757     
31758     
31759     this.addEvents({
31760         /**
31761          * @event keydown
31762          * Fires when a key is pressed
31763          * @param {Roo.BasicDialog} this
31764          * @param {Roo.EventObject} e
31765          */
31766         "keydown" : true,
31767         /**
31768          * @event move
31769          * Fires when this dialog is moved by the user.
31770          * @param {Roo.BasicDialog} this
31771          * @param {Number} x The new page X
31772          * @param {Number} y The new page Y
31773          */
31774         "move" : true,
31775         /**
31776          * @event resize
31777          * Fires when this dialog is resized by the user.
31778          * @param {Roo.BasicDialog} this
31779          * @param {Number} width The new width
31780          * @param {Number} height The new height
31781          */
31782         "resize" : true,
31783         /**
31784          * @event beforehide
31785          * Fires before this dialog is hidden.
31786          * @param {Roo.BasicDialog} this
31787          */
31788         "beforehide" : true,
31789         /**
31790          * @event hide
31791          * Fires when this dialog is hidden.
31792          * @param {Roo.BasicDialog} this
31793          */
31794         "hide" : true,
31795         /**
31796          * @event beforeshow
31797          * Fires before this dialog is shown.
31798          * @param {Roo.BasicDialog} this
31799          */
31800         "beforeshow" : true,
31801         /**
31802          * @event show
31803          * Fires when this dialog is shown.
31804          * @param {Roo.BasicDialog} this
31805          */
31806         "show" : true
31807     });
31808     el.on("keydown", this.onKeyDown, this);
31809     el.on("mousedown", this.toFront, this);
31810     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31811     this.el.hide();
31812     Roo.DialogManager.register(this);
31813     Roo.BasicDialog.superclass.constructor.call(this);
31814 };
31815
31816 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31817     shadowOffset: Roo.isIE ? 6 : 5,
31818     minHeight: 80,
31819     minWidth: 200,
31820     minButtonWidth: 75,
31821     defaultButton: null,
31822     buttonAlign: "right",
31823     tabTag: 'div',
31824     firstShow: true,
31825
31826     /**
31827      * Sets the dialog title text
31828      * @param {String} text The title text to display
31829      * @return {Roo.BasicDialog} this
31830      */
31831     setTitle : function(text){
31832         this.header.update(text);
31833         return this;
31834     },
31835
31836     // private
31837     closeClick : function(){
31838         this.hide();
31839     },
31840
31841     // private
31842     collapseClick : function(){
31843         this[this.collapsed ? "expand" : "collapse"]();
31844     },
31845
31846     /**
31847      * Collapses the dialog to its minimized state (only the title bar is visible).
31848      * Equivalent to the user clicking the collapse dialog button.
31849      */
31850     collapse : function(){
31851         if(!this.collapsed){
31852             this.collapsed = true;
31853             this.el.addClass("x-dlg-collapsed");
31854             this.restoreHeight = this.el.getHeight();
31855             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31856         }
31857     },
31858
31859     /**
31860      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
31861      * clicking the expand dialog button.
31862      */
31863     expand : function(){
31864         if(this.collapsed){
31865             this.collapsed = false;
31866             this.el.removeClass("x-dlg-collapsed");
31867             this.resizeTo(this.el.getWidth(), this.restoreHeight);
31868         }
31869     },
31870
31871     /**
31872      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
31873      * @return {Roo.TabPanel} The tabs component
31874      */
31875     initTabs : function(){
31876         var tabs = this.getTabs();
31877         while(tabs.getTab(0)){
31878             tabs.removeTab(0);
31879         }
31880         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
31881             var dom = el.dom;
31882             tabs.addTab(Roo.id(dom), dom.title);
31883             dom.title = "";
31884         });
31885         tabs.activate(0);
31886         return tabs;
31887     },
31888
31889     // private
31890     beforeResize : function(){
31891         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
31892     },
31893
31894     // private
31895     onResize : function(){
31896         this.refreshSize();
31897         this.syncBodyHeight();
31898         this.adjustAssets();
31899         this.focus();
31900         this.fireEvent("resize", this, this.size.width, this.size.height);
31901     },
31902
31903     // private
31904     onKeyDown : function(e){
31905         if(this.isVisible()){
31906             this.fireEvent("keydown", this, e);
31907         }
31908     },
31909
31910     /**
31911      * Resizes the dialog.
31912      * @param {Number} width
31913      * @param {Number} height
31914      * @return {Roo.BasicDialog} this
31915      */
31916     resizeTo : function(width, height){
31917         this.el.setSize(width, height);
31918         this.size = {width: width, height: height};
31919         this.syncBodyHeight();
31920         if(this.fixedcenter){
31921             this.center();
31922         }
31923         if(this.isVisible()){
31924             this.constrainXY();
31925             this.adjustAssets();
31926         }
31927         this.fireEvent("resize", this, width, height);
31928         return this;
31929     },
31930
31931
31932     /**
31933      * Resizes the dialog to fit the specified content size.
31934      * @param {Number} width
31935      * @param {Number} height
31936      * @return {Roo.BasicDialog} this
31937      */
31938     setContentSize : function(w, h){
31939         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
31940         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
31941         //if(!this.el.isBorderBox()){
31942             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
31943             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
31944         //}
31945         if(this.tabs){
31946             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
31947             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
31948         }
31949         this.resizeTo(w, h);
31950         return this;
31951     },
31952
31953     /**
31954      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
31955      * executed in response to a particular key being pressed while the dialog is active.
31956      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
31957      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
31958      * @param {Function} fn The function to call
31959      * @param {Object} scope (optional) The scope of the function
31960      * @return {Roo.BasicDialog} this
31961      */
31962     addKeyListener : function(key, fn, scope){
31963         var keyCode, shift, ctrl, alt;
31964         if(typeof key == "object" && !(key instanceof Array)){
31965             keyCode = key["key"];
31966             shift = key["shift"];
31967             ctrl = key["ctrl"];
31968             alt = key["alt"];
31969         }else{
31970             keyCode = key;
31971         }
31972         var handler = function(dlg, e){
31973             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
31974                 var k = e.getKey();
31975                 if(keyCode instanceof Array){
31976                     for(var i = 0, len = keyCode.length; i < len; i++){
31977                         if(keyCode[i] == k){
31978                           fn.call(scope || window, dlg, k, e);
31979                           return;
31980                         }
31981                     }
31982                 }else{
31983                     if(k == keyCode){
31984                         fn.call(scope || window, dlg, k, e);
31985                     }
31986                 }
31987             }
31988         };
31989         this.on("keydown", handler);
31990         return this;
31991     },
31992
31993     /**
31994      * Returns the TabPanel component (creates it if it doesn't exist).
31995      * Note: If you wish to simply check for the existence of tabs without creating them,
31996      * check for a null 'tabs' property.
31997      * @return {Roo.TabPanel} The tabs component
31998      */
31999     getTabs : function(){
32000         if(!this.tabs){
32001             this.el.addClass("x-dlg-auto-tabs");
32002             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32003             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32004         }
32005         return this.tabs;
32006     },
32007
32008     /**
32009      * Adds a button to the footer section of the dialog.
32010      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32011      * object or a valid Roo.DomHelper element config
32012      * @param {Function} handler The function called when the button is clicked
32013      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32014      * @return {Roo.Button} The new button
32015      */
32016     addButton : function(config, handler, scope){
32017         var dh = Roo.DomHelper;
32018         if(!this.footer){
32019             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32020         }
32021         if(!this.btnContainer){
32022             var tb = this.footer.createChild({
32023
32024                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32025                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32026             }, null, true);
32027             this.btnContainer = tb.firstChild.firstChild.firstChild;
32028         }
32029         var bconfig = {
32030             handler: handler,
32031             scope: scope,
32032             minWidth: this.minButtonWidth,
32033             hideParent:true
32034         };
32035         if(typeof config == "string"){
32036             bconfig.text = config;
32037         }else{
32038             if(config.tag){
32039                 bconfig.dhconfig = config;
32040             }else{
32041                 Roo.apply(bconfig, config);
32042             }
32043         }
32044         var fc = false;
32045         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32046             bconfig.position = Math.max(0, bconfig.position);
32047             fc = this.btnContainer.childNodes[bconfig.position];
32048         }
32049          
32050         var btn = new Roo.Button(
32051             fc ? 
32052                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32053                 : this.btnContainer.appendChild(document.createElement("td")),
32054             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32055             bconfig
32056         );
32057         this.syncBodyHeight();
32058         if(!this.buttons){
32059             /**
32060              * Array of all the buttons that have been added to this dialog via addButton
32061              * @type Array
32062              */
32063             this.buttons = [];
32064         }
32065         this.buttons.push(btn);
32066         return btn;
32067     },
32068
32069     /**
32070      * Sets the default button to be focused when the dialog is displayed.
32071      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32072      * @return {Roo.BasicDialog} this
32073      */
32074     setDefaultButton : function(btn){
32075         this.defaultButton = btn;
32076         return this;
32077     },
32078
32079     // private
32080     getHeaderFooterHeight : function(safe){
32081         var height = 0;
32082         if(this.header){
32083            height += this.header.getHeight();
32084         }
32085         if(this.footer){
32086            var fm = this.footer.getMargins();
32087             height += (this.footer.getHeight()+fm.top+fm.bottom);
32088         }
32089         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32090         height += this.centerBg.getPadding("tb");
32091         return height;
32092     },
32093
32094     // private
32095     syncBodyHeight : function()
32096     {
32097         var bd = this.body, // the text
32098             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32099             bw = this.bwrap;
32100         var height = this.size.height - this.getHeaderFooterHeight(false);
32101         bd.setHeight(height-bd.getMargins("tb"));
32102         var hh = this.header.getHeight();
32103         var h = this.size.height-hh;
32104         cb.setHeight(h);
32105         
32106         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32107         bw.setHeight(h-cb.getPadding("tb"));
32108         
32109         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32110         bd.setWidth(bw.getWidth(true));
32111         if(this.tabs){
32112             this.tabs.syncHeight();
32113             if(Roo.isIE){
32114                 this.tabs.el.repaint();
32115             }
32116         }
32117     },
32118
32119     /**
32120      * Restores the previous state of the dialog if Roo.state is configured.
32121      * @return {Roo.BasicDialog} this
32122      */
32123     restoreState : function(){
32124         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32125         if(box && box.width){
32126             this.xy = [box.x, box.y];
32127             this.resizeTo(box.width, box.height);
32128         }
32129         return this;
32130     },
32131
32132     // private
32133     beforeShow : function(){
32134         this.expand();
32135         if(this.fixedcenter){
32136             this.xy = this.el.getCenterXY(true);
32137         }
32138         if(this.modal){
32139             Roo.get(document.body).addClass("x-body-masked");
32140             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32141             this.mask.show();
32142         }
32143         this.constrainXY();
32144     },
32145
32146     // private
32147     animShow : function(){
32148         var b = Roo.get(this.animateTarget).getBox();
32149         this.proxy.setSize(b.width, b.height);
32150         this.proxy.setLocation(b.x, b.y);
32151         this.proxy.show();
32152         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32153                     true, .35, this.showEl.createDelegate(this));
32154     },
32155
32156     /**
32157      * Shows the dialog.
32158      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32159      * @return {Roo.BasicDialog} this
32160      */
32161     show : function(animateTarget){
32162         if (this.fireEvent("beforeshow", this) === false){
32163             return;
32164         }
32165         if(this.syncHeightBeforeShow){
32166             this.syncBodyHeight();
32167         }else if(this.firstShow){
32168             this.firstShow = false;
32169             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32170         }
32171         this.animateTarget = animateTarget || this.animateTarget;
32172         if(!this.el.isVisible()){
32173             this.beforeShow();
32174             if(this.animateTarget && Roo.get(this.animateTarget)){
32175                 this.animShow();
32176             }else{
32177                 this.showEl();
32178             }
32179         }
32180         return this;
32181     },
32182
32183     // private
32184     showEl : function(){
32185         this.proxy.hide();
32186         this.el.setXY(this.xy);
32187         this.el.show();
32188         this.adjustAssets(true);
32189         this.toFront();
32190         this.focus();
32191         // IE peekaboo bug - fix found by Dave Fenwick
32192         if(Roo.isIE){
32193             this.el.repaint();
32194         }
32195         this.fireEvent("show", this);
32196     },
32197
32198     /**
32199      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32200      * dialog itself will receive focus.
32201      */
32202     focus : function(){
32203         if(this.defaultButton){
32204             this.defaultButton.focus();
32205         }else{
32206             this.focusEl.focus();
32207         }
32208     },
32209
32210     // private
32211     constrainXY : function(){
32212         if(this.constraintoviewport !== false){
32213             if(!this.viewSize){
32214                 if(this.container){
32215                     var s = this.container.getSize();
32216                     this.viewSize = [s.width, s.height];
32217                 }else{
32218                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32219                 }
32220             }
32221             var s = Roo.get(this.container||document).getScroll();
32222
32223             var x = this.xy[0], y = this.xy[1];
32224             var w = this.size.width, h = this.size.height;
32225             var vw = this.viewSize[0], vh = this.viewSize[1];
32226             // only move it if it needs it
32227             var moved = false;
32228             // first validate right/bottom
32229             if(x + w > vw+s.left){
32230                 x = vw - w;
32231                 moved = true;
32232             }
32233             if(y + h > vh+s.top){
32234                 y = vh - h;
32235                 moved = true;
32236             }
32237             // then make sure top/left isn't negative
32238             if(x < s.left){
32239                 x = s.left;
32240                 moved = true;
32241             }
32242             if(y < s.top){
32243                 y = s.top;
32244                 moved = true;
32245             }
32246             if(moved){
32247                 // cache xy
32248                 this.xy = [x, y];
32249                 if(this.isVisible()){
32250                     this.el.setLocation(x, y);
32251                     this.adjustAssets();
32252                 }
32253             }
32254         }
32255     },
32256
32257     // private
32258     onDrag : function(){
32259         if(!this.proxyDrag){
32260             this.xy = this.el.getXY();
32261             this.adjustAssets();
32262         }
32263     },
32264
32265     // private
32266     adjustAssets : function(doShow){
32267         var x = this.xy[0], y = this.xy[1];
32268         var w = this.size.width, h = this.size.height;
32269         if(doShow === true){
32270             if(this.shadow){
32271                 this.shadow.show(this.el);
32272             }
32273             if(this.shim){
32274                 this.shim.show();
32275             }
32276         }
32277         if(this.shadow && this.shadow.isVisible()){
32278             this.shadow.show(this.el);
32279         }
32280         if(this.shim && this.shim.isVisible()){
32281             this.shim.setBounds(x, y, w, h);
32282         }
32283     },
32284
32285     // private
32286     adjustViewport : function(w, h){
32287         if(!w || !h){
32288             w = Roo.lib.Dom.getViewWidth();
32289             h = Roo.lib.Dom.getViewHeight();
32290         }
32291         // cache the size
32292         this.viewSize = [w, h];
32293         if(this.modal && this.mask.isVisible()){
32294             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32295             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32296         }
32297         if(this.isVisible()){
32298             this.constrainXY();
32299         }
32300     },
32301
32302     /**
32303      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32304      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32305      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32306      */
32307     destroy : function(removeEl){
32308         if(this.isVisible()){
32309             this.animateTarget = null;
32310             this.hide();
32311         }
32312         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32313         if(this.tabs){
32314             this.tabs.destroy(removeEl);
32315         }
32316         Roo.destroy(
32317              this.shim,
32318              this.proxy,
32319              this.resizer,
32320              this.close,
32321              this.mask
32322         );
32323         if(this.dd){
32324             this.dd.unreg();
32325         }
32326         if(this.buttons){
32327            for(var i = 0, len = this.buttons.length; i < len; i++){
32328                this.buttons[i].destroy();
32329            }
32330         }
32331         this.el.removeAllListeners();
32332         if(removeEl === true){
32333             this.el.update("");
32334             this.el.remove();
32335         }
32336         Roo.DialogManager.unregister(this);
32337     },
32338
32339     // private
32340     startMove : function(){
32341         if(this.proxyDrag){
32342             this.proxy.show();
32343         }
32344         if(this.constraintoviewport !== false){
32345             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32346         }
32347     },
32348
32349     // private
32350     endMove : function(){
32351         if(!this.proxyDrag){
32352             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32353         }else{
32354             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32355             this.proxy.hide();
32356         }
32357         this.refreshSize();
32358         this.adjustAssets();
32359         this.focus();
32360         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32361     },
32362
32363     /**
32364      * Brings this dialog to the front of any other visible dialogs
32365      * @return {Roo.BasicDialog} this
32366      */
32367     toFront : function(){
32368         Roo.DialogManager.bringToFront(this);
32369         return this;
32370     },
32371
32372     /**
32373      * Sends this dialog to the back (under) of any other visible dialogs
32374      * @return {Roo.BasicDialog} this
32375      */
32376     toBack : function(){
32377         Roo.DialogManager.sendToBack(this);
32378         return this;
32379     },
32380
32381     /**
32382      * Centers this dialog in the viewport
32383      * @return {Roo.BasicDialog} this
32384      */
32385     center : function(){
32386         var xy = this.el.getCenterXY(true);
32387         this.moveTo(xy[0], xy[1]);
32388         return this;
32389     },
32390
32391     /**
32392      * Moves the dialog's top-left corner to the specified point
32393      * @param {Number} x
32394      * @param {Number} y
32395      * @return {Roo.BasicDialog} this
32396      */
32397     moveTo : function(x, y){
32398         this.xy = [x,y];
32399         if(this.isVisible()){
32400             this.el.setXY(this.xy);
32401             this.adjustAssets();
32402         }
32403         return this;
32404     },
32405
32406     /**
32407      * Aligns the dialog to the specified element
32408      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32409      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32410      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32411      * @return {Roo.BasicDialog} this
32412      */
32413     alignTo : function(element, position, offsets){
32414         this.xy = this.el.getAlignToXY(element, position, offsets);
32415         if(this.isVisible()){
32416             this.el.setXY(this.xy);
32417             this.adjustAssets();
32418         }
32419         return this;
32420     },
32421
32422     /**
32423      * Anchors an element to another element and realigns it when the window is resized.
32424      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32425      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32426      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32427      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32428      * is a number, it is used as the buffer delay (defaults to 50ms).
32429      * @return {Roo.BasicDialog} this
32430      */
32431     anchorTo : function(el, alignment, offsets, monitorScroll){
32432         var action = function(){
32433             this.alignTo(el, alignment, offsets);
32434         };
32435         Roo.EventManager.onWindowResize(action, this);
32436         var tm = typeof monitorScroll;
32437         if(tm != 'undefined'){
32438             Roo.EventManager.on(window, 'scroll', action, this,
32439                 {buffer: tm == 'number' ? monitorScroll : 50});
32440         }
32441         action.call(this);
32442         return this;
32443     },
32444
32445     /**
32446      * Returns true if the dialog is visible
32447      * @return {Boolean}
32448      */
32449     isVisible : function(){
32450         return this.el.isVisible();
32451     },
32452
32453     // private
32454     animHide : function(callback){
32455         var b = Roo.get(this.animateTarget).getBox();
32456         this.proxy.show();
32457         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32458         this.el.hide();
32459         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32460                     this.hideEl.createDelegate(this, [callback]));
32461     },
32462
32463     /**
32464      * Hides the dialog.
32465      * @param {Function} callback (optional) Function to call when the dialog is hidden
32466      * @return {Roo.BasicDialog} this
32467      */
32468     hide : function(callback){
32469         if (this.fireEvent("beforehide", this) === false){
32470             return;
32471         }
32472         if(this.shadow){
32473             this.shadow.hide();
32474         }
32475         if(this.shim) {
32476           this.shim.hide();
32477         }
32478         // sometimes animateTarget seems to get set.. causing problems...
32479         // this just double checks..
32480         if(this.animateTarget && Roo.get(this.animateTarget)) {
32481            this.animHide(callback);
32482         }else{
32483             this.el.hide();
32484             this.hideEl(callback);
32485         }
32486         return this;
32487     },
32488
32489     // private
32490     hideEl : function(callback){
32491         this.proxy.hide();
32492         if(this.modal){
32493             this.mask.hide();
32494             Roo.get(document.body).removeClass("x-body-masked");
32495         }
32496         this.fireEvent("hide", this);
32497         if(typeof callback == "function"){
32498             callback();
32499         }
32500     },
32501
32502     // private
32503     hideAction : function(){
32504         this.setLeft("-10000px");
32505         this.setTop("-10000px");
32506         this.setStyle("visibility", "hidden");
32507     },
32508
32509     // private
32510     refreshSize : function(){
32511         this.size = this.el.getSize();
32512         this.xy = this.el.getXY();
32513         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32514     },
32515
32516     // private
32517     // z-index is managed by the DialogManager and may be overwritten at any time
32518     setZIndex : function(index){
32519         if(this.modal){
32520             this.mask.setStyle("z-index", index);
32521         }
32522         if(this.shim){
32523             this.shim.setStyle("z-index", ++index);
32524         }
32525         if(this.shadow){
32526             this.shadow.setZIndex(++index);
32527         }
32528         this.el.setStyle("z-index", ++index);
32529         if(this.proxy){
32530             this.proxy.setStyle("z-index", ++index);
32531         }
32532         if(this.resizer){
32533             this.resizer.proxy.setStyle("z-index", ++index);
32534         }
32535
32536         this.lastZIndex = index;
32537     },
32538
32539     /**
32540      * Returns the element for this dialog
32541      * @return {Roo.Element} The underlying dialog Element
32542      */
32543     getEl : function(){
32544         return this.el;
32545     }
32546 });
32547
32548 /**
32549  * @class Roo.DialogManager
32550  * Provides global access to BasicDialogs that have been created and
32551  * support for z-indexing (layering) multiple open dialogs.
32552  */
32553 Roo.DialogManager = function(){
32554     var list = {};
32555     var accessList = [];
32556     var front = null;
32557
32558     // private
32559     var sortDialogs = function(d1, d2){
32560         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32561     };
32562
32563     // private
32564     var orderDialogs = function(){
32565         accessList.sort(sortDialogs);
32566         var seed = Roo.DialogManager.zseed;
32567         for(var i = 0, len = accessList.length; i < len; i++){
32568             var dlg = accessList[i];
32569             if(dlg){
32570                 dlg.setZIndex(seed + (i*10));
32571             }
32572         }
32573     };
32574
32575     return {
32576         /**
32577          * The starting z-index for BasicDialogs (defaults to 9000)
32578          * @type Number The z-index value
32579          */
32580         zseed : 9000,
32581
32582         // private
32583         register : function(dlg){
32584             list[dlg.id] = dlg;
32585             accessList.push(dlg);
32586         },
32587
32588         // private
32589         unregister : function(dlg){
32590             delete list[dlg.id];
32591             var i=0;
32592             var len=0;
32593             if(!accessList.indexOf){
32594                 for(  i = 0, len = accessList.length; i < len; i++){
32595                     if(accessList[i] == dlg){
32596                         accessList.splice(i, 1);
32597                         return;
32598                     }
32599                 }
32600             }else{
32601                  i = accessList.indexOf(dlg);
32602                 if(i != -1){
32603                     accessList.splice(i, 1);
32604                 }
32605             }
32606         },
32607
32608         /**
32609          * Gets a registered dialog by id
32610          * @param {String/Object} id The id of the dialog or a dialog
32611          * @return {Roo.BasicDialog} this
32612          */
32613         get : function(id){
32614             return typeof id == "object" ? id : list[id];
32615         },
32616
32617         /**
32618          * Brings the specified dialog to the front
32619          * @param {String/Object} dlg The id of the dialog or a dialog
32620          * @return {Roo.BasicDialog} this
32621          */
32622         bringToFront : function(dlg){
32623             dlg = this.get(dlg);
32624             if(dlg != front){
32625                 front = dlg;
32626                 dlg._lastAccess = new Date().getTime();
32627                 orderDialogs();
32628             }
32629             return dlg;
32630         },
32631
32632         /**
32633          * Sends the specified dialog to the back
32634          * @param {String/Object} dlg The id of the dialog or a dialog
32635          * @return {Roo.BasicDialog} this
32636          */
32637         sendToBack : function(dlg){
32638             dlg = this.get(dlg);
32639             dlg._lastAccess = -(new Date().getTime());
32640             orderDialogs();
32641             return dlg;
32642         },
32643
32644         /**
32645          * Hides all dialogs
32646          */
32647         hideAll : function(){
32648             for(var id in list){
32649                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32650                     list[id].hide();
32651                 }
32652             }
32653         }
32654     };
32655 }();
32656
32657 /**
32658  * @class Roo.LayoutDialog
32659  * @extends Roo.BasicDialog
32660  * Dialog which provides adjustments for working with a layout in a Dialog.
32661  * Add your necessary layout config options to the dialog's config.<br>
32662  * Example usage (including a nested layout):
32663  * <pre><code>
32664 if(!dialog){
32665     dialog = new Roo.LayoutDialog("download-dlg", {
32666         modal: true,
32667         width:600,
32668         height:450,
32669         shadow:true,
32670         minWidth:500,
32671         minHeight:350,
32672         autoTabs:true,
32673         proxyDrag:true,
32674         // layout config merges with the dialog config
32675         center:{
32676             tabPosition: "top",
32677             alwaysShowTabs: true
32678         }
32679     });
32680     dialog.addKeyListener(27, dialog.hide, dialog);
32681     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32682     dialog.addButton("Build It!", this.getDownload, this);
32683
32684     // we can even add nested layouts
32685     var innerLayout = new Roo.BorderLayout("dl-inner", {
32686         east: {
32687             initialSize: 200,
32688             autoScroll:true,
32689             split:true
32690         },
32691         center: {
32692             autoScroll:true
32693         }
32694     });
32695     innerLayout.beginUpdate();
32696     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32697     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32698     innerLayout.endUpdate(true);
32699
32700     var layout = dialog.getLayout();
32701     layout.beginUpdate();
32702     layout.add("center", new Roo.ContentPanel("standard-panel",
32703                         {title: "Download the Source", fitToFrame:true}));
32704     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32705                {title: "Build your own roo.js"}));
32706     layout.getRegion("center").showPanel(sp);
32707     layout.endUpdate();
32708 }
32709 </code></pre>
32710     * @constructor
32711     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32712     * @param {Object} config configuration options
32713   */
32714 Roo.LayoutDialog = function(el, cfg){
32715     
32716     var config=  cfg;
32717     if (typeof(cfg) == 'undefined') {
32718         config = Roo.apply({}, el);
32719         // not sure why we use documentElement here.. - it should always be body.
32720         // IE7 borks horribly if we use documentElement.
32721         // webkit also does not like documentElement - it creates a body element...
32722         el = Roo.get( document.body || document.documentElement ).createChild();
32723         //config.autoCreate = true;
32724     }
32725     
32726     
32727     config.autoTabs = false;
32728     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32729     this.body.setStyle({overflow:"hidden", position:"relative"});
32730     this.layout = new Roo.BorderLayout(this.body.dom, config);
32731     this.layout.monitorWindowResize = false;
32732     this.el.addClass("x-dlg-auto-layout");
32733     // fix case when center region overwrites center function
32734     this.center = Roo.BasicDialog.prototype.center;
32735     this.on("show", this.layout.layout, this.layout, true);
32736     if (config.items) {
32737         var xitems = config.items;
32738         delete config.items;
32739         Roo.each(xitems, this.addxtype, this);
32740     }
32741     
32742     
32743 };
32744 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32745     /**
32746      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32747      * @deprecated
32748      */
32749     endUpdate : function(){
32750         this.layout.endUpdate();
32751     },
32752
32753     /**
32754      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32755      *  @deprecated
32756      */
32757     beginUpdate : function(){
32758         this.layout.beginUpdate();
32759     },
32760
32761     /**
32762      * Get the BorderLayout for this dialog
32763      * @return {Roo.BorderLayout}
32764      */
32765     getLayout : function(){
32766         return this.layout;
32767     },
32768
32769     showEl : function(){
32770         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32771         if(Roo.isIE7){
32772             this.layout.layout();
32773         }
32774     },
32775
32776     // private
32777     // Use the syncHeightBeforeShow config option to control this automatically
32778     syncBodyHeight : function(){
32779         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32780         if(this.layout){this.layout.layout();}
32781     },
32782     
32783       /**
32784      * Add an xtype element (actually adds to the layout.)
32785      * @return {Object} xdata xtype object data.
32786      */
32787     
32788     addxtype : function(c) {
32789         return this.layout.addxtype(c);
32790     }
32791 });/*
32792  * Based on:
32793  * Ext JS Library 1.1.1
32794  * Copyright(c) 2006-2007, Ext JS, LLC.
32795  *
32796  * Originally Released Under LGPL - original licence link has changed is not relivant.
32797  *
32798  * Fork - LGPL
32799  * <script type="text/javascript">
32800  */
32801  
32802 /**
32803  * @class Roo.MessageBox
32804  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32805  * Example usage:
32806  *<pre><code>
32807 // Basic alert:
32808 Roo.Msg.alert('Status', 'Changes saved successfully.');
32809
32810 // Prompt for user data:
32811 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32812     if (btn == 'ok'){
32813         // process text value...
32814     }
32815 });
32816
32817 // Show a dialog using config options:
32818 Roo.Msg.show({
32819    title:'Save Changes?',
32820    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32821    buttons: Roo.Msg.YESNOCANCEL,
32822    fn: processResult,
32823    animEl: 'elId'
32824 });
32825 </code></pre>
32826  * @singleton
32827  */
32828 Roo.MessageBox = function(){
32829     var dlg, opt, mask, waitTimer;
32830     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32831     var buttons, activeTextEl, bwidth;
32832
32833     // private
32834     var handleButton = function(button){
32835         dlg.hide();
32836         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32837     };
32838
32839     // private
32840     var handleHide = function(){
32841         if(opt && opt.cls){
32842             dlg.el.removeClass(opt.cls);
32843         }
32844         if(waitTimer){
32845             Roo.TaskMgr.stop(waitTimer);
32846             waitTimer = null;
32847         }
32848     };
32849
32850     // private
32851     var updateButtons = function(b){
32852         var width = 0;
32853         if(!b){
32854             buttons["ok"].hide();
32855             buttons["cancel"].hide();
32856             buttons["yes"].hide();
32857             buttons["no"].hide();
32858             dlg.footer.dom.style.display = 'none';
32859             return width;
32860         }
32861         dlg.footer.dom.style.display = '';
32862         for(var k in buttons){
32863             if(typeof buttons[k] != "function"){
32864                 if(b[k]){
32865                     buttons[k].show();
32866                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
32867                     width += buttons[k].el.getWidth()+15;
32868                 }else{
32869                     buttons[k].hide();
32870                 }
32871             }
32872         }
32873         return width;
32874     };
32875
32876     // private
32877     var handleEsc = function(d, k, e){
32878         if(opt && opt.closable !== false){
32879             dlg.hide();
32880         }
32881         if(e){
32882             e.stopEvent();
32883         }
32884     };
32885
32886     return {
32887         /**
32888          * Returns a reference to the underlying {@link Roo.BasicDialog} element
32889          * @return {Roo.BasicDialog} The BasicDialog element
32890          */
32891         getDialog : function(){
32892            if(!dlg){
32893                 dlg = new Roo.BasicDialog("x-msg-box", {
32894                     autoCreate : true,
32895                     shadow: true,
32896                     draggable: true,
32897                     resizable:false,
32898                     constraintoviewport:false,
32899                     fixedcenter:true,
32900                     collapsible : false,
32901                     shim:true,
32902                     modal: true,
32903                     width:400, height:100,
32904                     buttonAlign:"center",
32905                     closeClick : function(){
32906                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
32907                             handleButton("no");
32908                         }else{
32909                             handleButton("cancel");
32910                         }
32911                     }
32912                 });
32913                 dlg.on("hide", handleHide);
32914                 mask = dlg.mask;
32915                 dlg.addKeyListener(27, handleEsc);
32916                 buttons = {};
32917                 var bt = this.buttonText;
32918                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
32919                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
32920                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
32921                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
32922                 bodyEl = dlg.body.createChild({
32923
32924                     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>'
32925                 });
32926                 msgEl = bodyEl.dom.firstChild;
32927                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
32928                 textboxEl.enableDisplayMode();
32929                 textboxEl.addKeyListener([10,13], function(){
32930                     if(dlg.isVisible() && opt && opt.buttons){
32931                         if(opt.buttons.ok){
32932                             handleButton("ok");
32933                         }else if(opt.buttons.yes){
32934                             handleButton("yes");
32935                         }
32936                     }
32937                 });
32938                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
32939                 textareaEl.enableDisplayMode();
32940                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
32941                 progressEl.enableDisplayMode();
32942                 var pf = progressEl.dom.firstChild;
32943                 if (pf) {
32944                     pp = Roo.get(pf.firstChild);
32945                     pp.setHeight(pf.offsetHeight);
32946                 }
32947                 
32948             }
32949             return dlg;
32950         },
32951
32952         /**
32953          * Updates the message box body text
32954          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
32955          * the XHTML-compliant non-breaking space character '&amp;#160;')
32956          * @return {Roo.MessageBox} This message box
32957          */
32958         updateText : function(text){
32959             if(!dlg.isVisible() && !opt.width){
32960                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
32961             }
32962             msgEl.innerHTML = text || '&#160;';
32963       
32964             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
32965             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
32966             var w = Math.max(
32967                     Math.min(opt.width || cw , this.maxWidth), 
32968                     Math.max(opt.minWidth || this.minWidth, bwidth)
32969             );
32970             if(opt.prompt){
32971                 activeTextEl.setWidth(w);
32972             }
32973             if(dlg.isVisible()){
32974                 dlg.fixedcenter = false;
32975             }
32976             // to big, make it scroll. = But as usual stupid IE does not support
32977             // !important..
32978             
32979             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
32980                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
32981                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
32982             } else {
32983                 bodyEl.dom.style.height = '';
32984                 bodyEl.dom.style.overflowY = '';
32985             }
32986             if (cw > w) {
32987                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
32988             } else {
32989                 bodyEl.dom.style.overflowX = '';
32990             }
32991             
32992             dlg.setContentSize(w, bodyEl.getHeight());
32993             if(dlg.isVisible()){
32994                 dlg.fixedcenter = true;
32995             }
32996             return this;
32997         },
32998
32999         /**
33000          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33001          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33002          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33003          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33004          * @return {Roo.MessageBox} This message box
33005          */
33006         updateProgress : function(value, text){
33007             if(text){
33008                 this.updateText(text);
33009             }
33010             if (pp) { // weird bug on my firefox - for some reason this is not defined
33011                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33012             }
33013             return this;
33014         },        
33015
33016         /**
33017          * Returns true if the message box is currently displayed
33018          * @return {Boolean} True if the message box is visible, else false
33019          */
33020         isVisible : function(){
33021             return dlg && dlg.isVisible();  
33022         },
33023
33024         /**
33025          * Hides the message box if it is displayed
33026          */
33027         hide : function(){
33028             if(this.isVisible()){
33029                 dlg.hide();
33030             }  
33031         },
33032
33033         /**
33034          * Displays a new message box, or reinitializes an existing message box, based on the config options
33035          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33036          * The following config object properties are supported:
33037          * <pre>
33038 Property    Type             Description
33039 ----------  ---------------  ------------------------------------------------------------------------------------
33040 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33041                                    closes (defaults to undefined)
33042 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33043                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33044 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33045                                    progress and wait dialogs will ignore this property and always hide the
33046                                    close button as they can only be closed programmatically.
33047 cls               String           A custom CSS class to apply to the message box element
33048 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33049                                    displayed (defaults to 75)
33050 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33051                                    function will be btn (the name of the button that was clicked, if applicable,
33052                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33053                                    Progress and wait dialogs will ignore this option since they do not respond to
33054                                    user actions and can only be closed programmatically, so any required function
33055                                    should be called by the same code after it closes the dialog.
33056 icon              String           A CSS class that provides a background image to be used as an icon for
33057                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33058 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33059 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33060 modal             Boolean          False to allow user interaction with the page while the message box is
33061                                    displayed (defaults to true)
33062 msg               String           A string that will replace the existing message box body text (defaults
33063                                    to the XHTML-compliant non-breaking space character '&#160;')
33064 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33065 progress          Boolean          True to display a progress bar (defaults to false)
33066 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33067 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33068 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33069 title             String           The title text
33070 value             String           The string value to set into the active textbox element if displayed
33071 wait              Boolean          True to display a progress bar (defaults to false)
33072 width             Number           The width of the dialog in pixels
33073 </pre>
33074          *
33075          * Example usage:
33076          * <pre><code>
33077 Roo.Msg.show({
33078    title: 'Address',
33079    msg: 'Please enter your address:',
33080    width: 300,
33081    buttons: Roo.MessageBox.OKCANCEL,
33082    multiline: true,
33083    fn: saveAddress,
33084    animEl: 'addAddressBtn'
33085 });
33086 </code></pre>
33087          * @param {Object} config Configuration options
33088          * @return {Roo.MessageBox} This message box
33089          */
33090         show : function(options)
33091         {
33092             
33093             // this causes nightmares if you show one dialog after another
33094             // especially on callbacks..
33095              
33096             if(this.isVisible()){
33097                 
33098                 this.hide();
33099                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33100                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33101                 Roo.log("New Dialog Message:" +  options.msg )
33102                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33103                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33104                 
33105             }
33106             var d = this.getDialog();
33107             opt = options;
33108             d.setTitle(opt.title || "&#160;");
33109             d.close.setDisplayed(opt.closable !== false);
33110             activeTextEl = textboxEl;
33111             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33112             if(opt.prompt){
33113                 if(opt.multiline){
33114                     textboxEl.hide();
33115                     textareaEl.show();
33116                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33117                         opt.multiline : this.defaultTextHeight);
33118                     activeTextEl = textareaEl;
33119                 }else{
33120                     textboxEl.show();
33121                     textareaEl.hide();
33122                 }
33123             }else{
33124                 textboxEl.hide();
33125                 textareaEl.hide();
33126             }
33127             progressEl.setDisplayed(opt.progress === true);
33128             this.updateProgress(0);
33129             activeTextEl.dom.value = opt.value || "";
33130             if(opt.prompt){
33131                 dlg.setDefaultButton(activeTextEl);
33132             }else{
33133                 var bs = opt.buttons;
33134                 var db = null;
33135                 if(bs && bs.ok){
33136                     db = buttons["ok"];
33137                 }else if(bs && bs.yes){
33138                     db = buttons["yes"];
33139                 }
33140                 dlg.setDefaultButton(db);
33141             }
33142             bwidth = updateButtons(opt.buttons);
33143             this.updateText(opt.msg);
33144             if(opt.cls){
33145                 d.el.addClass(opt.cls);
33146             }
33147             d.proxyDrag = opt.proxyDrag === true;
33148             d.modal = opt.modal !== false;
33149             d.mask = opt.modal !== false ? mask : false;
33150             if(!d.isVisible()){
33151                 // force it to the end of the z-index stack so it gets a cursor in FF
33152                 document.body.appendChild(dlg.el.dom);
33153                 d.animateTarget = null;
33154                 d.show(options.animEl);
33155             }
33156             return this;
33157         },
33158
33159         /**
33160          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33161          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33162          * and closing the message box when the process is complete.
33163          * @param {String} title The title bar text
33164          * @param {String} msg The message box body text
33165          * @return {Roo.MessageBox} This message box
33166          */
33167         progress : function(title, msg){
33168             this.show({
33169                 title : title,
33170                 msg : msg,
33171                 buttons: false,
33172                 progress:true,
33173                 closable:false,
33174                 minWidth: this.minProgressWidth,
33175                 modal : true
33176             });
33177             return this;
33178         },
33179
33180         /**
33181          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33182          * If a callback function is passed it will be called after the user clicks the button, and the
33183          * id of the button that was clicked will be passed as the only parameter to the callback
33184          * (could also be the top-right close button).
33185          * @param {String} title The title bar text
33186          * @param {String} msg The message box body text
33187          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33188          * @param {Object} scope (optional) The scope of the callback function
33189          * @return {Roo.MessageBox} This message box
33190          */
33191         alert : function(title, msg, fn, scope){
33192             this.show({
33193                 title : title,
33194                 msg : msg,
33195                 buttons: this.OK,
33196                 fn: fn,
33197                 scope : scope,
33198                 modal : true
33199             });
33200             return this;
33201         },
33202
33203         /**
33204          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33205          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33206          * You are responsible for closing the message box when the process is complete.
33207          * @param {String} msg The message box body text
33208          * @param {String} title (optional) The title bar text
33209          * @return {Roo.MessageBox} This message box
33210          */
33211         wait : function(msg, title){
33212             this.show({
33213                 title : title,
33214                 msg : msg,
33215                 buttons: false,
33216                 closable:false,
33217                 progress:true,
33218                 modal:true,
33219                 width:300,
33220                 wait:true
33221             });
33222             waitTimer = Roo.TaskMgr.start({
33223                 run: function(i){
33224                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33225                 },
33226                 interval: 1000
33227             });
33228             return this;
33229         },
33230
33231         /**
33232          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33233          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33234          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33235          * @param {String} title The title bar text
33236          * @param {String} msg The message box body text
33237          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33238          * @param {Object} scope (optional) The scope of the callback function
33239          * @return {Roo.MessageBox} This message box
33240          */
33241         confirm : function(title, msg, fn, scope){
33242             this.show({
33243                 title : title,
33244                 msg : msg,
33245                 buttons: this.YESNO,
33246                 fn: fn,
33247                 scope : scope,
33248                 modal : true
33249             });
33250             return this;
33251         },
33252
33253         /**
33254          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33255          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33256          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33257          * (could also be the top-right close button) and the text that was entered will be passed as the two
33258          * parameters to the callback.
33259          * @param {String} title The title bar text
33260          * @param {String} msg The message box body text
33261          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33262          * @param {Object} scope (optional) The scope of the callback function
33263          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33264          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33265          * @return {Roo.MessageBox} This message box
33266          */
33267         prompt : function(title, msg, fn, scope, multiline){
33268             this.show({
33269                 title : title,
33270                 msg : msg,
33271                 buttons: this.OKCANCEL,
33272                 fn: fn,
33273                 minWidth:250,
33274                 scope : scope,
33275                 prompt:true,
33276                 multiline: multiline,
33277                 modal : true
33278             });
33279             return this;
33280         },
33281
33282         /**
33283          * Button config that displays a single OK button
33284          * @type Object
33285          */
33286         OK : {ok:true},
33287         /**
33288          * Button config that displays Yes and No buttons
33289          * @type Object
33290          */
33291         YESNO : {yes:true, no:true},
33292         /**
33293          * Button config that displays OK and Cancel buttons
33294          * @type Object
33295          */
33296         OKCANCEL : {ok:true, cancel:true},
33297         /**
33298          * Button config that displays Yes, No and Cancel buttons
33299          * @type Object
33300          */
33301         YESNOCANCEL : {yes:true, no:true, cancel:true},
33302
33303         /**
33304          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33305          * @type Number
33306          */
33307         defaultTextHeight : 75,
33308         /**
33309          * The maximum width in pixels of the message box (defaults to 600)
33310          * @type Number
33311          */
33312         maxWidth : 600,
33313         /**
33314          * The minimum width in pixels of the message box (defaults to 100)
33315          * @type Number
33316          */
33317         minWidth : 100,
33318         /**
33319          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33320          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33321          * @type Number
33322          */
33323         minProgressWidth : 250,
33324         /**
33325          * An object containing the default button text strings that can be overriden for localized language support.
33326          * Supported properties are: ok, cancel, yes and no.
33327          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33328          * @type Object
33329          */
33330         buttonText : {
33331             ok : "OK",
33332             cancel : "Cancel",
33333             yes : "Yes",
33334             no : "No"
33335         }
33336     };
33337 }();
33338
33339 /**
33340  * Shorthand for {@link Roo.MessageBox}
33341  */
33342 Roo.Msg = Roo.MessageBox;/*
33343  * Based on:
33344  * Ext JS Library 1.1.1
33345  * Copyright(c) 2006-2007, Ext JS, LLC.
33346  *
33347  * Originally Released Under LGPL - original licence link has changed is not relivant.
33348  *
33349  * Fork - LGPL
33350  * <script type="text/javascript">
33351  */
33352 /**
33353  * @class Roo.QuickTips
33354  * Provides attractive and customizable tooltips for any element.
33355  * @singleton
33356  */
33357 Roo.QuickTips = function(){
33358     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33359     var ce, bd, xy, dd;
33360     var visible = false, disabled = true, inited = false;
33361     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33362     
33363     var onOver = function(e){
33364         if(disabled){
33365             return;
33366         }
33367         var t = e.getTarget();
33368         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33369             return;
33370         }
33371         if(ce && t == ce.el){
33372             clearTimeout(hideProc);
33373             return;
33374         }
33375         if(t && tagEls[t.id]){
33376             tagEls[t.id].el = t;
33377             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33378             return;
33379         }
33380         var ttp, et = Roo.fly(t);
33381         var ns = cfg.namespace;
33382         if(tm.interceptTitles && t.title){
33383             ttp = t.title;
33384             t.qtip = ttp;
33385             t.removeAttribute("title");
33386             e.preventDefault();
33387         }else{
33388             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33389         }
33390         if(ttp){
33391             showProc = show.defer(tm.showDelay, tm, [{
33392                 el: t, 
33393                 text: ttp, 
33394                 width: et.getAttributeNS(ns, cfg.width),
33395                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33396                 title: et.getAttributeNS(ns, cfg.title),
33397                     cls: et.getAttributeNS(ns, cfg.cls)
33398             }]);
33399         }
33400     };
33401     
33402     var onOut = function(e){
33403         clearTimeout(showProc);
33404         var t = e.getTarget();
33405         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33406             hideProc = setTimeout(hide, tm.hideDelay);
33407         }
33408     };
33409     
33410     var onMove = function(e){
33411         if(disabled){
33412             return;
33413         }
33414         xy = e.getXY();
33415         xy[1] += 18;
33416         if(tm.trackMouse && ce){
33417             el.setXY(xy);
33418         }
33419     };
33420     
33421     var onDown = function(e){
33422         clearTimeout(showProc);
33423         clearTimeout(hideProc);
33424         if(!e.within(el)){
33425             if(tm.hideOnClick){
33426                 hide();
33427                 tm.disable();
33428                 tm.enable.defer(100, tm);
33429             }
33430         }
33431     };
33432     
33433     var getPad = function(){
33434         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33435     };
33436
33437     var show = function(o){
33438         if(disabled){
33439             return;
33440         }
33441         clearTimeout(dismissProc);
33442         ce = o;
33443         if(removeCls){ // in case manually hidden
33444             el.removeClass(removeCls);
33445             removeCls = null;
33446         }
33447         if(ce.cls){
33448             el.addClass(ce.cls);
33449             removeCls = ce.cls;
33450         }
33451         if(ce.title){
33452             tipTitle.update(ce.title);
33453             tipTitle.show();
33454         }else{
33455             tipTitle.update('');
33456             tipTitle.hide();
33457         }
33458         el.dom.style.width  = tm.maxWidth+'px';
33459         //tipBody.dom.style.width = '';
33460         tipBodyText.update(o.text);
33461         var p = getPad(), w = ce.width;
33462         if(!w){
33463             var td = tipBodyText.dom;
33464             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33465             if(aw > tm.maxWidth){
33466                 w = tm.maxWidth;
33467             }else if(aw < tm.minWidth){
33468                 w = tm.minWidth;
33469             }else{
33470                 w = aw;
33471             }
33472         }
33473         //tipBody.setWidth(w);
33474         el.setWidth(parseInt(w, 10) + p);
33475         if(ce.autoHide === false){
33476             close.setDisplayed(true);
33477             if(dd){
33478                 dd.unlock();
33479             }
33480         }else{
33481             close.setDisplayed(false);
33482             if(dd){
33483                 dd.lock();
33484             }
33485         }
33486         if(xy){
33487             el.avoidY = xy[1]-18;
33488             el.setXY(xy);
33489         }
33490         if(tm.animate){
33491             el.setOpacity(.1);
33492             el.setStyle("visibility", "visible");
33493             el.fadeIn({callback: afterShow});
33494         }else{
33495             afterShow();
33496         }
33497     };
33498     
33499     var afterShow = function(){
33500         if(ce){
33501             el.show();
33502             esc.enable();
33503             if(tm.autoDismiss && ce.autoHide !== false){
33504                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33505             }
33506         }
33507     };
33508     
33509     var hide = function(noanim){
33510         clearTimeout(dismissProc);
33511         clearTimeout(hideProc);
33512         ce = null;
33513         if(el.isVisible()){
33514             esc.disable();
33515             if(noanim !== true && tm.animate){
33516                 el.fadeOut({callback: afterHide});
33517             }else{
33518                 afterHide();
33519             } 
33520         }
33521     };
33522     
33523     var afterHide = function(){
33524         el.hide();
33525         if(removeCls){
33526             el.removeClass(removeCls);
33527             removeCls = null;
33528         }
33529     };
33530     
33531     return {
33532         /**
33533         * @cfg {Number} minWidth
33534         * The minimum width of the quick tip (defaults to 40)
33535         */
33536        minWidth : 40,
33537         /**
33538         * @cfg {Number} maxWidth
33539         * The maximum width of the quick tip (defaults to 300)
33540         */
33541        maxWidth : 300,
33542         /**
33543         * @cfg {Boolean} interceptTitles
33544         * True to automatically use the element's DOM title value if available (defaults to false)
33545         */
33546        interceptTitles : false,
33547         /**
33548         * @cfg {Boolean} trackMouse
33549         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33550         */
33551        trackMouse : false,
33552         /**
33553         * @cfg {Boolean} hideOnClick
33554         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33555         */
33556        hideOnClick : true,
33557         /**
33558         * @cfg {Number} showDelay
33559         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33560         */
33561        showDelay : 500,
33562         /**
33563         * @cfg {Number} hideDelay
33564         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33565         */
33566        hideDelay : 200,
33567         /**
33568         * @cfg {Boolean} autoHide
33569         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33570         * Used in conjunction with hideDelay.
33571         */
33572        autoHide : true,
33573         /**
33574         * @cfg {Boolean}
33575         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33576         * (defaults to true).  Used in conjunction with autoDismissDelay.
33577         */
33578        autoDismiss : true,
33579         /**
33580         * @cfg {Number}
33581         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33582         */
33583        autoDismissDelay : 5000,
33584        /**
33585         * @cfg {Boolean} animate
33586         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33587         */
33588        animate : false,
33589
33590        /**
33591         * @cfg {String} title
33592         * Title text to display (defaults to '').  This can be any valid HTML markup.
33593         */
33594         title: '',
33595        /**
33596         * @cfg {String} text
33597         * Body text to display (defaults to '').  This can be any valid HTML markup.
33598         */
33599         text : '',
33600        /**
33601         * @cfg {String} cls
33602         * A CSS class to apply to the base quick tip element (defaults to '').
33603         */
33604         cls : '',
33605        /**
33606         * @cfg {Number} width
33607         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33608         * minWidth or maxWidth.
33609         */
33610         width : null,
33611
33612     /**
33613      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33614      * or display QuickTips in a page.
33615      */
33616        init : function(){
33617           tm = Roo.QuickTips;
33618           cfg = tm.tagConfig;
33619           if(!inited){
33620               if(!Roo.isReady){ // allow calling of init() before onReady
33621                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33622                   return;
33623               }
33624               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33625               el.fxDefaults = {stopFx: true};
33626               // maximum custom styling
33627               //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>');
33628               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>');              
33629               tipTitle = el.child('h3');
33630               tipTitle.enableDisplayMode("block");
33631               tipBody = el.child('div.x-tip-bd');
33632               tipBodyText = el.child('div.x-tip-bd-inner');
33633               //bdLeft = el.child('div.x-tip-bd-left');
33634               //bdRight = el.child('div.x-tip-bd-right');
33635               close = el.child('div.x-tip-close');
33636               close.enableDisplayMode("block");
33637               close.on("click", hide);
33638               var d = Roo.get(document);
33639               d.on("mousedown", onDown);
33640               d.on("mouseover", onOver);
33641               d.on("mouseout", onOut);
33642               d.on("mousemove", onMove);
33643               esc = d.addKeyListener(27, hide);
33644               esc.disable();
33645               if(Roo.dd.DD){
33646                   dd = el.initDD("default", null, {
33647                       onDrag : function(){
33648                           el.sync();  
33649                       }
33650                   });
33651                   dd.setHandleElId(tipTitle.id);
33652                   dd.lock();
33653               }
33654               inited = true;
33655           }
33656           this.enable(); 
33657        },
33658
33659     /**
33660      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33661      * are supported:
33662      * <pre>
33663 Property    Type                   Description
33664 ----------  ---------------------  ------------------------------------------------------------------------
33665 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33666      * </ul>
33667      * @param {Object} config The config object
33668      */
33669        register : function(config){
33670            var cs = config instanceof Array ? config : arguments;
33671            for(var i = 0, len = cs.length; i < len; i++) {
33672                var c = cs[i];
33673                var target = c.target;
33674                if(target){
33675                    if(target instanceof Array){
33676                        for(var j = 0, jlen = target.length; j < jlen; j++){
33677                            tagEls[target[j]] = c;
33678                        }
33679                    }else{
33680                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33681                    }
33682                }
33683            }
33684        },
33685
33686     /**
33687      * Removes this quick tip from its element and destroys it.
33688      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33689      */
33690        unregister : function(el){
33691            delete tagEls[Roo.id(el)];
33692        },
33693
33694     /**
33695      * Enable this quick tip.
33696      */
33697        enable : function(){
33698            if(inited && disabled){
33699                locks.pop();
33700                if(locks.length < 1){
33701                    disabled = false;
33702                }
33703            }
33704        },
33705
33706     /**
33707      * Disable this quick tip.
33708      */
33709        disable : function(){
33710           disabled = true;
33711           clearTimeout(showProc);
33712           clearTimeout(hideProc);
33713           clearTimeout(dismissProc);
33714           if(ce){
33715               hide(true);
33716           }
33717           locks.push(1);
33718        },
33719
33720     /**
33721      * Returns true if the quick tip is enabled, else false.
33722      */
33723        isEnabled : function(){
33724             return !disabled;
33725        },
33726
33727         // private
33728        tagConfig : {
33729            namespace : "roo", // was ext?? this may break..
33730            alt_namespace : "ext",
33731            attribute : "qtip",
33732            width : "width",
33733            target : "target",
33734            title : "qtitle",
33735            hide : "hide",
33736            cls : "qclass"
33737        }
33738    };
33739 }();
33740
33741 // backwards compat
33742 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33743  * Based on:
33744  * Ext JS Library 1.1.1
33745  * Copyright(c) 2006-2007, Ext JS, LLC.
33746  *
33747  * Originally Released Under LGPL - original licence link has changed is not relivant.
33748  *
33749  * Fork - LGPL
33750  * <script type="text/javascript">
33751  */
33752  
33753
33754 /**
33755  * @class Roo.tree.TreePanel
33756  * @extends Roo.data.Tree
33757
33758  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33759  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33760  * @cfg {Boolean} enableDD true to enable drag and drop
33761  * @cfg {Boolean} enableDrag true to enable just drag
33762  * @cfg {Boolean} enableDrop true to enable just drop
33763  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33764  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33765  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33766  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33767  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33768  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33769  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33770  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33771  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33772  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33773  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33774  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33775  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33776  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33777  * @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>
33778  * @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>
33779  * 
33780  * @constructor
33781  * @param {String/HTMLElement/Element} el The container element
33782  * @param {Object} config
33783  */
33784 Roo.tree.TreePanel = function(el, config){
33785     var root = false;
33786     var loader = false;
33787     if (config.root) {
33788         root = config.root;
33789         delete config.root;
33790     }
33791     if (config.loader) {
33792         loader = config.loader;
33793         delete config.loader;
33794     }
33795     
33796     Roo.apply(this, config);
33797     Roo.tree.TreePanel.superclass.constructor.call(this);
33798     this.el = Roo.get(el);
33799     this.el.addClass('x-tree');
33800     //console.log(root);
33801     if (root) {
33802         this.setRootNode( Roo.factory(root, Roo.tree));
33803     }
33804     if (loader) {
33805         this.loader = Roo.factory(loader, Roo.tree);
33806     }
33807    /**
33808     * Read-only. The id of the container element becomes this TreePanel's id.
33809     */
33810     this.id = this.el.id;
33811     this.addEvents({
33812         /**
33813         * @event beforeload
33814         * Fires before a node is loaded, return false to cancel
33815         * @param {Node} node The node being loaded
33816         */
33817         "beforeload" : true,
33818         /**
33819         * @event load
33820         * Fires when a node is loaded
33821         * @param {Node} node The node that was loaded
33822         */
33823         "load" : true,
33824         /**
33825         * @event textchange
33826         * Fires when the text for a node is changed
33827         * @param {Node} node The node
33828         * @param {String} text The new text
33829         * @param {String} oldText The old text
33830         */
33831         "textchange" : true,
33832         /**
33833         * @event beforeexpand
33834         * Fires before a node is expanded, return false to cancel.
33835         * @param {Node} node The node
33836         * @param {Boolean} deep
33837         * @param {Boolean} anim
33838         */
33839         "beforeexpand" : true,
33840         /**
33841         * @event beforecollapse
33842         * Fires before a node is collapsed, return false to cancel.
33843         * @param {Node} node The node
33844         * @param {Boolean} deep
33845         * @param {Boolean} anim
33846         */
33847         "beforecollapse" : true,
33848         /**
33849         * @event expand
33850         * Fires when a node is expanded
33851         * @param {Node} node The node
33852         */
33853         "expand" : true,
33854         /**
33855         * @event disabledchange
33856         * Fires when the disabled status of a node changes
33857         * @param {Node} node The node
33858         * @param {Boolean} disabled
33859         */
33860         "disabledchange" : true,
33861         /**
33862         * @event collapse
33863         * Fires when a node is collapsed
33864         * @param {Node} node The node
33865         */
33866         "collapse" : true,
33867         /**
33868         * @event beforeclick
33869         * Fires before click processing on a node. Return false to cancel the default action.
33870         * @param {Node} node The node
33871         * @param {Roo.EventObject} e The event object
33872         */
33873         "beforeclick":true,
33874         /**
33875         * @event checkchange
33876         * Fires when a node with a checkbox's checked property changes
33877         * @param {Node} this This node
33878         * @param {Boolean} checked
33879         */
33880         "checkchange":true,
33881         /**
33882         * @event click
33883         * Fires when a node is clicked
33884         * @param {Node} node The node
33885         * @param {Roo.EventObject} e The event object
33886         */
33887         "click":true,
33888         /**
33889         * @event dblclick
33890         * Fires when a node is double clicked
33891         * @param {Node} node The node
33892         * @param {Roo.EventObject} e The event object
33893         */
33894         "dblclick":true,
33895         /**
33896         * @event contextmenu
33897         * Fires when a node is right clicked
33898         * @param {Node} node The node
33899         * @param {Roo.EventObject} e The event object
33900         */
33901         "contextmenu":true,
33902         /**
33903         * @event beforechildrenrendered
33904         * Fires right before the child nodes for a node are rendered
33905         * @param {Node} node The node
33906         */
33907         "beforechildrenrendered":true,
33908         /**
33909         * @event startdrag
33910         * Fires when a node starts being dragged
33911         * @param {Roo.tree.TreePanel} this
33912         * @param {Roo.tree.TreeNode} node
33913         * @param {event} e The raw browser event
33914         */ 
33915        "startdrag" : true,
33916        /**
33917         * @event enddrag
33918         * Fires when a drag operation is complete
33919         * @param {Roo.tree.TreePanel} this
33920         * @param {Roo.tree.TreeNode} node
33921         * @param {event} e The raw browser event
33922         */
33923        "enddrag" : true,
33924        /**
33925         * @event dragdrop
33926         * Fires when a dragged node is dropped on a valid DD target
33927         * @param {Roo.tree.TreePanel} this
33928         * @param {Roo.tree.TreeNode} node
33929         * @param {DD} dd The dd it was dropped on
33930         * @param {event} e The raw browser event
33931         */
33932        "dragdrop" : true,
33933        /**
33934         * @event beforenodedrop
33935         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
33936         * passed to handlers has the following properties:<br />
33937         * <ul style="padding:5px;padding-left:16px;">
33938         * <li>tree - The TreePanel</li>
33939         * <li>target - The node being targeted for the drop</li>
33940         * <li>data - The drag data from the drag source</li>
33941         * <li>point - The point of the drop - append, above or below</li>
33942         * <li>source - The drag source</li>
33943         * <li>rawEvent - Raw mouse event</li>
33944         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
33945         * to be inserted by setting them on this object.</li>
33946         * <li>cancel - Set this to true to cancel the drop.</li>
33947         * </ul>
33948         * @param {Object} dropEvent
33949         */
33950        "beforenodedrop" : true,
33951        /**
33952         * @event nodedrop
33953         * Fires after a DD object is dropped on a node in this tree. The dropEvent
33954         * passed to handlers has the following properties:<br />
33955         * <ul style="padding:5px;padding-left:16px;">
33956         * <li>tree - The TreePanel</li>
33957         * <li>target - The node being targeted for the drop</li>
33958         * <li>data - The drag data from the drag source</li>
33959         * <li>point - The point of the drop - append, above or below</li>
33960         * <li>source - The drag source</li>
33961         * <li>rawEvent - Raw mouse event</li>
33962         * <li>dropNode - Dropped node(s).</li>
33963         * </ul>
33964         * @param {Object} dropEvent
33965         */
33966        "nodedrop" : true,
33967         /**
33968         * @event nodedragover
33969         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
33970         * passed to handlers has the following properties:<br />
33971         * <ul style="padding:5px;padding-left:16px;">
33972         * <li>tree - The TreePanel</li>
33973         * <li>target - The node being targeted for the drop</li>
33974         * <li>data - The drag data from the drag source</li>
33975         * <li>point - The point of the drop - append, above or below</li>
33976         * <li>source - The drag source</li>
33977         * <li>rawEvent - Raw mouse event</li>
33978         * <li>dropNode - Drop node(s) provided by the source.</li>
33979         * <li>cancel - Set this to true to signal drop not allowed.</li>
33980         * </ul>
33981         * @param {Object} dragOverEvent
33982         */
33983        "nodedragover" : true
33984         
33985     });
33986     if(this.singleExpand){
33987        this.on("beforeexpand", this.restrictExpand, this);
33988     }
33989     if (this.editor) {
33990         this.editor.tree = this;
33991         this.editor = Roo.factory(this.editor, Roo.tree);
33992     }
33993     
33994     if (this.selModel) {
33995         this.selModel = Roo.factory(this.selModel, Roo.tree);
33996     }
33997    
33998 };
33999 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34000     rootVisible : true,
34001     animate: Roo.enableFx,
34002     lines : true,
34003     enableDD : false,
34004     hlDrop : Roo.enableFx,
34005   
34006     renderer: false,
34007     
34008     rendererTip: false,
34009     // private
34010     restrictExpand : function(node){
34011         var p = node.parentNode;
34012         if(p){
34013             if(p.expandedChild && p.expandedChild.parentNode == p){
34014                 p.expandedChild.collapse();
34015             }
34016             p.expandedChild = node;
34017         }
34018     },
34019
34020     // private override
34021     setRootNode : function(node){
34022         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34023         if(!this.rootVisible){
34024             node.ui = new Roo.tree.RootTreeNodeUI(node);
34025         }
34026         return node;
34027     },
34028
34029     /**
34030      * Returns the container element for this TreePanel
34031      */
34032     getEl : function(){
34033         return this.el;
34034     },
34035
34036     /**
34037      * Returns the default TreeLoader for this TreePanel
34038      */
34039     getLoader : function(){
34040         return this.loader;
34041     },
34042
34043     /**
34044      * Expand all nodes
34045      */
34046     expandAll : function(){
34047         this.root.expand(true);
34048     },
34049
34050     /**
34051      * Collapse all nodes
34052      */
34053     collapseAll : function(){
34054         this.root.collapse(true);
34055     },
34056
34057     /**
34058      * Returns the selection model used by this TreePanel
34059      */
34060     getSelectionModel : function(){
34061         if(!this.selModel){
34062             this.selModel = new Roo.tree.DefaultSelectionModel();
34063         }
34064         return this.selModel;
34065     },
34066
34067     /**
34068      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34069      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34070      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34071      * @return {Array}
34072      */
34073     getChecked : function(a, startNode){
34074         startNode = startNode || this.root;
34075         var r = [];
34076         var f = function(){
34077             if(this.attributes.checked){
34078                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34079             }
34080         }
34081         startNode.cascade(f);
34082         return r;
34083     },
34084
34085     /**
34086      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34087      * @param {String} path
34088      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34089      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34090      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34091      */
34092     expandPath : function(path, attr, callback){
34093         attr = attr || "id";
34094         var keys = path.split(this.pathSeparator);
34095         var curNode = this.root;
34096         if(curNode.attributes[attr] != keys[1]){ // invalid root
34097             if(callback){
34098                 callback(false, null);
34099             }
34100             return;
34101         }
34102         var index = 1;
34103         var f = function(){
34104             if(++index == keys.length){
34105                 if(callback){
34106                     callback(true, curNode);
34107                 }
34108                 return;
34109             }
34110             var c = curNode.findChild(attr, keys[index]);
34111             if(!c){
34112                 if(callback){
34113                     callback(false, curNode);
34114                 }
34115                 return;
34116             }
34117             curNode = c;
34118             c.expand(false, false, f);
34119         };
34120         curNode.expand(false, false, f);
34121     },
34122
34123     /**
34124      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34125      * @param {String} path
34126      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34127      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34128      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34129      */
34130     selectPath : function(path, attr, callback){
34131         attr = attr || "id";
34132         var keys = path.split(this.pathSeparator);
34133         var v = keys.pop();
34134         if(keys.length > 0){
34135             var f = function(success, node){
34136                 if(success && node){
34137                     var n = node.findChild(attr, v);
34138                     if(n){
34139                         n.select();
34140                         if(callback){
34141                             callback(true, n);
34142                         }
34143                     }else if(callback){
34144                         callback(false, n);
34145                     }
34146                 }else{
34147                     if(callback){
34148                         callback(false, n);
34149                     }
34150                 }
34151             };
34152             this.expandPath(keys.join(this.pathSeparator), attr, f);
34153         }else{
34154             this.root.select();
34155             if(callback){
34156                 callback(true, this.root);
34157             }
34158         }
34159     },
34160
34161     getTreeEl : function(){
34162         return this.el;
34163     },
34164
34165     /**
34166      * Trigger rendering of this TreePanel
34167      */
34168     render : function(){
34169         if (this.innerCt) {
34170             return this; // stop it rendering more than once!!
34171         }
34172         
34173         this.innerCt = this.el.createChild({tag:"ul",
34174                cls:"x-tree-root-ct " +
34175                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34176
34177         if(this.containerScroll){
34178             Roo.dd.ScrollManager.register(this.el);
34179         }
34180         if((this.enableDD || this.enableDrop) && !this.dropZone){
34181            /**
34182             * The dropZone used by this tree if drop is enabled
34183             * @type Roo.tree.TreeDropZone
34184             */
34185              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34186                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34187            });
34188         }
34189         if((this.enableDD || this.enableDrag) && !this.dragZone){
34190            /**
34191             * The dragZone used by this tree if drag is enabled
34192             * @type Roo.tree.TreeDragZone
34193             */
34194             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34195                ddGroup: this.ddGroup || "TreeDD",
34196                scroll: this.ddScroll
34197            });
34198         }
34199         this.getSelectionModel().init(this);
34200         if (!this.root) {
34201             Roo.log("ROOT not set in tree");
34202             return this;
34203         }
34204         this.root.render();
34205         if(!this.rootVisible){
34206             this.root.renderChildren();
34207         }
34208         return this;
34209     }
34210 });/*
34211  * Based on:
34212  * Ext JS Library 1.1.1
34213  * Copyright(c) 2006-2007, Ext JS, LLC.
34214  *
34215  * Originally Released Under LGPL - original licence link has changed is not relivant.
34216  *
34217  * Fork - LGPL
34218  * <script type="text/javascript">
34219  */
34220  
34221
34222 /**
34223  * @class Roo.tree.DefaultSelectionModel
34224  * @extends Roo.util.Observable
34225  * The default single selection for a TreePanel.
34226  * @param {Object} cfg Configuration
34227  */
34228 Roo.tree.DefaultSelectionModel = function(cfg){
34229    this.selNode = null;
34230    
34231    
34232    
34233    this.addEvents({
34234        /**
34235         * @event selectionchange
34236         * Fires when the selected node changes
34237         * @param {DefaultSelectionModel} this
34238         * @param {TreeNode} node the new selection
34239         */
34240        "selectionchange" : true,
34241
34242        /**
34243         * @event beforeselect
34244         * Fires before the selected node changes, return false to cancel the change
34245         * @param {DefaultSelectionModel} this
34246         * @param {TreeNode} node the new selection
34247         * @param {TreeNode} node the old selection
34248         */
34249        "beforeselect" : true
34250    });
34251    
34252     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34253 };
34254
34255 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34256     init : function(tree){
34257         this.tree = tree;
34258         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34259         tree.on("click", this.onNodeClick, this);
34260     },
34261     
34262     onNodeClick : function(node, e){
34263         if (e.ctrlKey && this.selNode == node)  {
34264             this.unselect(node);
34265             return;
34266         }
34267         this.select(node);
34268     },
34269     
34270     /**
34271      * Select a node.
34272      * @param {TreeNode} node The node to select
34273      * @return {TreeNode} The selected node
34274      */
34275     select : function(node){
34276         var last = this.selNode;
34277         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34278             if(last){
34279                 last.ui.onSelectedChange(false);
34280             }
34281             this.selNode = node;
34282             node.ui.onSelectedChange(true);
34283             this.fireEvent("selectionchange", this, node, last);
34284         }
34285         return node;
34286     },
34287     
34288     /**
34289      * Deselect a node.
34290      * @param {TreeNode} node The node to unselect
34291      */
34292     unselect : function(node){
34293         if(this.selNode == node){
34294             this.clearSelections();
34295         }    
34296     },
34297     
34298     /**
34299      * Clear all selections
34300      */
34301     clearSelections : function(){
34302         var n = this.selNode;
34303         if(n){
34304             n.ui.onSelectedChange(false);
34305             this.selNode = null;
34306             this.fireEvent("selectionchange", this, null);
34307         }
34308         return n;
34309     },
34310     
34311     /**
34312      * Get the selected node
34313      * @return {TreeNode} The selected node
34314      */
34315     getSelectedNode : function(){
34316         return this.selNode;    
34317     },
34318     
34319     /**
34320      * Returns true if the node is selected
34321      * @param {TreeNode} node The node to check
34322      * @return {Boolean}
34323      */
34324     isSelected : function(node){
34325         return this.selNode == node;  
34326     },
34327
34328     /**
34329      * Selects the node above the selected node in the tree, intelligently walking the nodes
34330      * @return TreeNode The new selection
34331      */
34332     selectPrevious : function(){
34333         var s = this.selNode || this.lastSelNode;
34334         if(!s){
34335             return null;
34336         }
34337         var ps = s.previousSibling;
34338         if(ps){
34339             if(!ps.isExpanded() || ps.childNodes.length < 1){
34340                 return this.select(ps);
34341             } else{
34342                 var lc = ps.lastChild;
34343                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34344                     lc = lc.lastChild;
34345                 }
34346                 return this.select(lc);
34347             }
34348         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34349             return this.select(s.parentNode);
34350         }
34351         return null;
34352     },
34353
34354     /**
34355      * Selects the node above the selected node in the tree, intelligently walking the nodes
34356      * @return TreeNode The new selection
34357      */
34358     selectNext : function(){
34359         var s = this.selNode || this.lastSelNode;
34360         if(!s){
34361             return null;
34362         }
34363         if(s.firstChild && s.isExpanded()){
34364              return this.select(s.firstChild);
34365          }else if(s.nextSibling){
34366              return this.select(s.nextSibling);
34367          }else if(s.parentNode){
34368             var newS = null;
34369             s.parentNode.bubble(function(){
34370                 if(this.nextSibling){
34371                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34372                     return false;
34373                 }
34374             });
34375             return newS;
34376          }
34377         return null;
34378     },
34379
34380     onKeyDown : function(e){
34381         var s = this.selNode || this.lastSelNode;
34382         // undesirable, but required
34383         var sm = this;
34384         if(!s){
34385             return;
34386         }
34387         var k = e.getKey();
34388         switch(k){
34389              case e.DOWN:
34390                  e.stopEvent();
34391                  this.selectNext();
34392              break;
34393              case e.UP:
34394                  e.stopEvent();
34395                  this.selectPrevious();
34396              break;
34397              case e.RIGHT:
34398                  e.preventDefault();
34399                  if(s.hasChildNodes()){
34400                      if(!s.isExpanded()){
34401                          s.expand();
34402                      }else if(s.firstChild){
34403                          this.select(s.firstChild, e);
34404                      }
34405                  }
34406              break;
34407              case e.LEFT:
34408                  e.preventDefault();
34409                  if(s.hasChildNodes() && s.isExpanded()){
34410                      s.collapse();
34411                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34412                      this.select(s.parentNode, e);
34413                  }
34414              break;
34415         };
34416     }
34417 });
34418
34419 /**
34420  * @class Roo.tree.MultiSelectionModel
34421  * @extends Roo.util.Observable
34422  * Multi selection for a TreePanel.
34423  * @param {Object} cfg Configuration
34424  */
34425 Roo.tree.MultiSelectionModel = function(){
34426    this.selNodes = [];
34427    this.selMap = {};
34428    this.addEvents({
34429        /**
34430         * @event selectionchange
34431         * Fires when the selected nodes change
34432         * @param {MultiSelectionModel} this
34433         * @param {Array} nodes Array of the selected nodes
34434         */
34435        "selectionchange" : true
34436    });
34437    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34438    
34439 };
34440
34441 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34442     init : function(tree){
34443         this.tree = tree;
34444         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34445         tree.on("click", this.onNodeClick, this);
34446     },
34447     
34448     onNodeClick : function(node, e){
34449         this.select(node, e, e.ctrlKey);
34450     },
34451     
34452     /**
34453      * Select a node.
34454      * @param {TreeNode} node The node to select
34455      * @param {EventObject} e (optional) An event associated with the selection
34456      * @param {Boolean} keepExisting True to retain existing selections
34457      * @return {TreeNode} The selected node
34458      */
34459     select : function(node, e, keepExisting){
34460         if(keepExisting !== true){
34461             this.clearSelections(true);
34462         }
34463         if(this.isSelected(node)){
34464             this.lastSelNode = node;
34465             return node;
34466         }
34467         this.selNodes.push(node);
34468         this.selMap[node.id] = node;
34469         this.lastSelNode = node;
34470         node.ui.onSelectedChange(true);
34471         this.fireEvent("selectionchange", this, this.selNodes);
34472         return node;
34473     },
34474     
34475     /**
34476      * Deselect a node.
34477      * @param {TreeNode} node The node to unselect
34478      */
34479     unselect : function(node){
34480         if(this.selMap[node.id]){
34481             node.ui.onSelectedChange(false);
34482             var sn = this.selNodes;
34483             var index = -1;
34484             if(sn.indexOf){
34485                 index = sn.indexOf(node);
34486             }else{
34487                 for(var i = 0, len = sn.length; i < len; i++){
34488                     if(sn[i] == node){
34489                         index = i;
34490                         break;
34491                     }
34492                 }
34493             }
34494             if(index != -1){
34495                 this.selNodes.splice(index, 1);
34496             }
34497             delete this.selMap[node.id];
34498             this.fireEvent("selectionchange", this, this.selNodes);
34499         }
34500     },
34501     
34502     /**
34503      * Clear all selections
34504      */
34505     clearSelections : function(suppressEvent){
34506         var sn = this.selNodes;
34507         if(sn.length > 0){
34508             for(var i = 0, len = sn.length; i < len; i++){
34509                 sn[i].ui.onSelectedChange(false);
34510             }
34511             this.selNodes = [];
34512             this.selMap = {};
34513             if(suppressEvent !== true){
34514                 this.fireEvent("selectionchange", this, this.selNodes);
34515             }
34516         }
34517     },
34518     
34519     /**
34520      * Returns true if the node is selected
34521      * @param {TreeNode} node The node to check
34522      * @return {Boolean}
34523      */
34524     isSelected : function(node){
34525         return this.selMap[node.id] ? true : false;  
34526     },
34527     
34528     /**
34529      * Returns an array of the selected nodes
34530      * @return {Array}
34531      */
34532     getSelectedNodes : function(){
34533         return this.selNodes;    
34534     },
34535
34536     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34537
34538     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34539
34540     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34541 });/*
34542  * Based on:
34543  * Ext JS Library 1.1.1
34544  * Copyright(c) 2006-2007, Ext JS, LLC.
34545  *
34546  * Originally Released Under LGPL - original licence link has changed is not relivant.
34547  *
34548  * Fork - LGPL
34549  * <script type="text/javascript">
34550  */
34551  
34552 /**
34553  * @class Roo.tree.TreeNode
34554  * @extends Roo.data.Node
34555  * @cfg {String} text The text for this node
34556  * @cfg {Boolean} expanded true to start the node expanded
34557  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34558  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34559  * @cfg {Boolean} disabled true to start the node disabled
34560  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34561  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34562  * @cfg {String} cls A css class to be added to the node
34563  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34564  * @cfg {String} href URL of the link used for the node (defaults to #)
34565  * @cfg {String} hrefTarget target frame for the link
34566  * @cfg {String} qtip An Ext QuickTip for the node
34567  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34568  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34569  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34570  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34571  * (defaults to undefined with no checkbox rendered)
34572  * @constructor
34573  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34574  */
34575 Roo.tree.TreeNode = function(attributes){
34576     attributes = attributes || {};
34577     if(typeof attributes == "string"){
34578         attributes = {text: attributes};
34579     }
34580     this.childrenRendered = false;
34581     this.rendered = false;
34582     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34583     this.expanded = attributes.expanded === true;
34584     this.isTarget = attributes.isTarget !== false;
34585     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34586     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34587
34588     /**
34589      * Read-only. The text for this node. To change it use setText().
34590      * @type String
34591      */
34592     this.text = attributes.text;
34593     /**
34594      * True if this node is disabled.
34595      * @type Boolean
34596      */
34597     this.disabled = attributes.disabled === true;
34598
34599     this.addEvents({
34600         /**
34601         * @event textchange
34602         * Fires when the text for this node is changed
34603         * @param {Node} this This node
34604         * @param {String} text The new text
34605         * @param {String} oldText The old text
34606         */
34607         "textchange" : true,
34608         /**
34609         * @event beforeexpand
34610         * Fires before this node is expanded, return false to cancel.
34611         * @param {Node} this This node
34612         * @param {Boolean} deep
34613         * @param {Boolean} anim
34614         */
34615         "beforeexpand" : true,
34616         /**
34617         * @event beforecollapse
34618         * Fires before this node is collapsed, return false to cancel.
34619         * @param {Node} this This node
34620         * @param {Boolean} deep
34621         * @param {Boolean} anim
34622         */
34623         "beforecollapse" : true,
34624         /**
34625         * @event expand
34626         * Fires when this node is expanded
34627         * @param {Node} this This node
34628         */
34629         "expand" : true,
34630         /**
34631         * @event disabledchange
34632         * Fires when the disabled status of this node changes
34633         * @param {Node} this This node
34634         * @param {Boolean} disabled
34635         */
34636         "disabledchange" : true,
34637         /**
34638         * @event collapse
34639         * Fires when this node is collapsed
34640         * @param {Node} this This node
34641         */
34642         "collapse" : true,
34643         /**
34644         * @event beforeclick
34645         * Fires before click processing. Return false to cancel the default action.
34646         * @param {Node} this This node
34647         * @param {Roo.EventObject} e The event object
34648         */
34649         "beforeclick":true,
34650         /**
34651         * @event checkchange
34652         * Fires when a node with a checkbox's checked property changes
34653         * @param {Node} this This node
34654         * @param {Boolean} checked
34655         */
34656         "checkchange":true,
34657         /**
34658         * @event click
34659         * Fires when this node is clicked
34660         * @param {Node} this This node
34661         * @param {Roo.EventObject} e The event object
34662         */
34663         "click":true,
34664         /**
34665         * @event dblclick
34666         * Fires when this node is double clicked
34667         * @param {Node} this This node
34668         * @param {Roo.EventObject} e The event object
34669         */
34670         "dblclick":true,
34671         /**
34672         * @event contextmenu
34673         * Fires when this node is right clicked
34674         * @param {Node} this This node
34675         * @param {Roo.EventObject} e The event object
34676         */
34677         "contextmenu":true,
34678         /**
34679         * @event beforechildrenrendered
34680         * Fires right before the child nodes for this node are rendered
34681         * @param {Node} this This node
34682         */
34683         "beforechildrenrendered":true
34684     });
34685
34686     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34687
34688     /**
34689      * Read-only. The UI for this node
34690      * @type TreeNodeUI
34691      */
34692     this.ui = new uiClass(this);
34693     
34694     // finally support items[]
34695     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34696         return;
34697     }
34698     
34699     
34700     Roo.each(this.attributes.items, function(c) {
34701         this.appendChild(Roo.factory(c,Roo.Tree));
34702     }, this);
34703     delete this.attributes.items;
34704     
34705     
34706     
34707 };
34708 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34709     preventHScroll: true,
34710     /**
34711      * Returns true if this node is expanded
34712      * @return {Boolean}
34713      */
34714     isExpanded : function(){
34715         return this.expanded;
34716     },
34717
34718     /**
34719      * Returns the UI object for this node
34720      * @return {TreeNodeUI}
34721      */
34722     getUI : function(){
34723         return this.ui;
34724     },
34725
34726     // private override
34727     setFirstChild : function(node){
34728         var of = this.firstChild;
34729         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34730         if(this.childrenRendered && of && node != of){
34731             of.renderIndent(true, true);
34732         }
34733         if(this.rendered){
34734             this.renderIndent(true, true);
34735         }
34736     },
34737
34738     // private override
34739     setLastChild : function(node){
34740         var ol = this.lastChild;
34741         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34742         if(this.childrenRendered && ol && node != ol){
34743             ol.renderIndent(true, true);
34744         }
34745         if(this.rendered){
34746             this.renderIndent(true, true);
34747         }
34748     },
34749
34750     // these methods are overridden to provide lazy rendering support
34751     // private override
34752     appendChild : function()
34753     {
34754         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34755         if(node && this.childrenRendered){
34756             node.render();
34757         }
34758         this.ui.updateExpandIcon();
34759         return node;
34760     },
34761
34762     // private override
34763     removeChild : function(node){
34764         this.ownerTree.getSelectionModel().unselect(node);
34765         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34766         // if it's been rendered remove dom node
34767         if(this.childrenRendered){
34768             node.ui.remove();
34769         }
34770         if(this.childNodes.length < 1){
34771             this.collapse(false, false);
34772         }else{
34773             this.ui.updateExpandIcon();
34774         }
34775         if(!this.firstChild) {
34776             this.childrenRendered = false;
34777         }
34778         return node;
34779     },
34780
34781     // private override
34782     insertBefore : function(node, refNode){
34783         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34784         if(newNode && refNode && this.childrenRendered){
34785             node.render();
34786         }
34787         this.ui.updateExpandIcon();
34788         return newNode;
34789     },
34790
34791     /**
34792      * Sets the text for this node
34793      * @param {String} text
34794      */
34795     setText : function(text){
34796         var oldText = this.text;
34797         this.text = text;
34798         this.attributes.text = text;
34799         if(this.rendered){ // event without subscribing
34800             this.ui.onTextChange(this, text, oldText);
34801         }
34802         this.fireEvent("textchange", this, text, oldText);
34803     },
34804
34805     /**
34806      * Triggers selection of this node
34807      */
34808     select : function(){
34809         this.getOwnerTree().getSelectionModel().select(this);
34810     },
34811
34812     /**
34813      * Triggers deselection of this node
34814      */
34815     unselect : function(){
34816         this.getOwnerTree().getSelectionModel().unselect(this);
34817     },
34818
34819     /**
34820      * Returns true if this node is selected
34821      * @return {Boolean}
34822      */
34823     isSelected : function(){
34824         return this.getOwnerTree().getSelectionModel().isSelected(this);
34825     },
34826
34827     /**
34828      * Expand this node.
34829      * @param {Boolean} deep (optional) True to expand all children as well
34830      * @param {Boolean} anim (optional) false to cancel the default animation
34831      * @param {Function} callback (optional) A callback to be called when
34832      * expanding this node completes (does not wait for deep expand to complete).
34833      * Called with 1 parameter, this node.
34834      */
34835     expand : function(deep, anim, callback){
34836         if(!this.expanded){
34837             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34838                 return;
34839             }
34840             if(!this.childrenRendered){
34841                 this.renderChildren();
34842             }
34843             this.expanded = true;
34844             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34845                 this.ui.animExpand(function(){
34846                     this.fireEvent("expand", this);
34847                     if(typeof callback == "function"){
34848                         callback(this);
34849                     }
34850                     if(deep === true){
34851                         this.expandChildNodes(true);
34852                     }
34853                 }.createDelegate(this));
34854                 return;
34855             }else{
34856                 this.ui.expand();
34857                 this.fireEvent("expand", this);
34858                 if(typeof callback == "function"){
34859                     callback(this);
34860                 }
34861             }
34862         }else{
34863            if(typeof callback == "function"){
34864                callback(this);
34865            }
34866         }
34867         if(deep === true){
34868             this.expandChildNodes(true);
34869         }
34870     },
34871
34872     isHiddenRoot : function(){
34873         return this.isRoot && !this.getOwnerTree().rootVisible;
34874     },
34875
34876     /**
34877      * Collapse this node.
34878      * @param {Boolean} deep (optional) True to collapse all children as well
34879      * @param {Boolean} anim (optional) false to cancel the default animation
34880      */
34881     collapse : function(deep, anim){
34882         if(this.expanded && !this.isHiddenRoot()){
34883             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
34884                 return;
34885             }
34886             this.expanded = false;
34887             if((this.getOwnerTree().animate && anim !== false) || anim){
34888                 this.ui.animCollapse(function(){
34889                     this.fireEvent("collapse", this);
34890                     if(deep === true){
34891                         this.collapseChildNodes(true);
34892                     }
34893                 }.createDelegate(this));
34894                 return;
34895             }else{
34896                 this.ui.collapse();
34897                 this.fireEvent("collapse", this);
34898             }
34899         }
34900         if(deep === true){
34901             var cs = this.childNodes;
34902             for(var i = 0, len = cs.length; i < len; i++) {
34903                 cs[i].collapse(true, false);
34904             }
34905         }
34906     },
34907
34908     // private
34909     delayedExpand : function(delay){
34910         if(!this.expandProcId){
34911             this.expandProcId = this.expand.defer(delay, this);
34912         }
34913     },
34914
34915     // private
34916     cancelExpand : function(){
34917         if(this.expandProcId){
34918             clearTimeout(this.expandProcId);
34919         }
34920         this.expandProcId = false;
34921     },
34922
34923     /**
34924      * Toggles expanded/collapsed state of the node
34925      */
34926     toggle : function(){
34927         if(this.expanded){
34928             this.collapse();
34929         }else{
34930             this.expand();
34931         }
34932     },
34933
34934     /**
34935      * Ensures all parent nodes are expanded
34936      */
34937     ensureVisible : function(callback){
34938         var tree = this.getOwnerTree();
34939         tree.expandPath(this.parentNode.getPath(), false, function(){
34940             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
34941             Roo.callback(callback);
34942         }.createDelegate(this));
34943     },
34944
34945     /**
34946      * Expand all child nodes
34947      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
34948      */
34949     expandChildNodes : function(deep){
34950         var cs = this.childNodes;
34951         for(var i = 0, len = cs.length; i < len; i++) {
34952                 cs[i].expand(deep);
34953         }
34954     },
34955
34956     /**
34957      * Collapse all child nodes
34958      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
34959      */
34960     collapseChildNodes : function(deep){
34961         var cs = this.childNodes;
34962         for(var i = 0, len = cs.length; i < len; i++) {
34963                 cs[i].collapse(deep);
34964         }
34965     },
34966
34967     /**
34968      * Disables this node
34969      */
34970     disable : function(){
34971         this.disabled = true;
34972         this.unselect();
34973         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
34974             this.ui.onDisableChange(this, true);
34975         }
34976         this.fireEvent("disabledchange", this, true);
34977     },
34978
34979     /**
34980      * Enables this node
34981      */
34982     enable : function(){
34983         this.disabled = false;
34984         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
34985             this.ui.onDisableChange(this, false);
34986         }
34987         this.fireEvent("disabledchange", this, false);
34988     },
34989
34990     // private
34991     renderChildren : function(suppressEvent){
34992         if(suppressEvent !== false){
34993             this.fireEvent("beforechildrenrendered", this);
34994         }
34995         var cs = this.childNodes;
34996         for(var i = 0, len = cs.length; i < len; i++){
34997             cs[i].render(true);
34998         }
34999         this.childrenRendered = true;
35000     },
35001
35002     // private
35003     sort : function(fn, scope){
35004         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35005         if(this.childrenRendered){
35006             var cs = this.childNodes;
35007             for(var i = 0, len = cs.length; i < len; i++){
35008                 cs[i].render(true);
35009             }
35010         }
35011     },
35012
35013     // private
35014     render : function(bulkRender){
35015         this.ui.render(bulkRender);
35016         if(!this.rendered){
35017             this.rendered = true;
35018             if(this.expanded){
35019                 this.expanded = false;
35020                 this.expand(false, false);
35021             }
35022         }
35023     },
35024
35025     // private
35026     renderIndent : function(deep, refresh){
35027         if(refresh){
35028             this.ui.childIndent = null;
35029         }
35030         this.ui.renderIndent();
35031         if(deep === true && this.childrenRendered){
35032             var cs = this.childNodes;
35033             for(var i = 0, len = cs.length; i < len; i++){
35034                 cs[i].renderIndent(true, refresh);
35035             }
35036         }
35037     }
35038 });/*
35039  * Based on:
35040  * Ext JS Library 1.1.1
35041  * Copyright(c) 2006-2007, Ext JS, LLC.
35042  *
35043  * Originally Released Under LGPL - original licence link has changed is not relivant.
35044  *
35045  * Fork - LGPL
35046  * <script type="text/javascript">
35047  */
35048  
35049 /**
35050  * @class Roo.tree.AsyncTreeNode
35051  * @extends Roo.tree.TreeNode
35052  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35053  * @constructor
35054  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35055  */
35056  Roo.tree.AsyncTreeNode = function(config){
35057     this.loaded = false;
35058     this.loading = false;
35059     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35060     /**
35061     * @event beforeload
35062     * Fires before this node is loaded, return false to cancel
35063     * @param {Node} this This node
35064     */
35065     this.addEvents({'beforeload':true, 'load': true});
35066     /**
35067     * @event load
35068     * Fires when this node is loaded
35069     * @param {Node} this This node
35070     */
35071     /**
35072      * The loader used by this node (defaults to using the tree's defined loader)
35073      * @type TreeLoader
35074      * @property loader
35075      */
35076 };
35077 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35078     expand : function(deep, anim, callback){
35079         if(this.loading){ // if an async load is already running, waiting til it's done
35080             var timer;
35081             var f = function(){
35082                 if(!this.loading){ // done loading
35083                     clearInterval(timer);
35084                     this.expand(deep, anim, callback);
35085                 }
35086             }.createDelegate(this);
35087             timer = setInterval(f, 200);
35088             return;
35089         }
35090         if(!this.loaded){
35091             if(this.fireEvent("beforeload", this) === false){
35092                 return;
35093             }
35094             this.loading = true;
35095             this.ui.beforeLoad(this);
35096             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35097             if(loader){
35098                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35099                 return;
35100             }
35101         }
35102         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35103     },
35104     
35105     /**
35106      * Returns true if this node is currently loading
35107      * @return {Boolean}
35108      */
35109     isLoading : function(){
35110         return this.loading;  
35111     },
35112     
35113     loadComplete : function(deep, anim, callback){
35114         this.loading = false;
35115         this.loaded = true;
35116         this.ui.afterLoad(this);
35117         this.fireEvent("load", this);
35118         this.expand(deep, anim, callback);
35119     },
35120     
35121     /**
35122      * Returns true if this node has been loaded
35123      * @return {Boolean}
35124      */
35125     isLoaded : function(){
35126         return this.loaded;
35127     },
35128     
35129     hasChildNodes : function(){
35130         if(!this.isLeaf() && !this.loaded){
35131             return true;
35132         }else{
35133             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35134         }
35135     },
35136
35137     /**
35138      * Trigger a reload for this node
35139      * @param {Function} callback
35140      */
35141     reload : function(callback){
35142         this.collapse(false, false);
35143         while(this.firstChild){
35144             this.removeChild(this.firstChild);
35145         }
35146         this.childrenRendered = false;
35147         this.loaded = false;
35148         if(this.isHiddenRoot()){
35149             this.expanded = false;
35150         }
35151         this.expand(false, false, callback);
35152     }
35153 });/*
35154  * Based on:
35155  * Ext JS Library 1.1.1
35156  * Copyright(c) 2006-2007, Ext JS, LLC.
35157  *
35158  * Originally Released Under LGPL - original licence link has changed is not relivant.
35159  *
35160  * Fork - LGPL
35161  * <script type="text/javascript">
35162  */
35163  
35164 /**
35165  * @class Roo.tree.TreeNodeUI
35166  * @constructor
35167  * @param {Object} node The node to render
35168  * The TreeNode UI implementation is separate from the
35169  * tree implementation. Unless you are customizing the tree UI,
35170  * you should never have to use this directly.
35171  */
35172 Roo.tree.TreeNodeUI = function(node){
35173     this.node = node;
35174     this.rendered = false;
35175     this.animating = false;
35176     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35177 };
35178
35179 Roo.tree.TreeNodeUI.prototype = {
35180     removeChild : function(node){
35181         if(this.rendered){
35182             this.ctNode.removeChild(node.ui.getEl());
35183         }
35184     },
35185
35186     beforeLoad : function(){
35187          this.addClass("x-tree-node-loading");
35188     },
35189
35190     afterLoad : function(){
35191          this.removeClass("x-tree-node-loading");
35192     },
35193
35194     onTextChange : function(node, text, oldText){
35195         if(this.rendered){
35196             this.textNode.innerHTML = text;
35197         }
35198     },
35199
35200     onDisableChange : function(node, state){
35201         this.disabled = state;
35202         if(state){
35203             this.addClass("x-tree-node-disabled");
35204         }else{
35205             this.removeClass("x-tree-node-disabled");
35206         }
35207     },
35208
35209     onSelectedChange : function(state){
35210         if(state){
35211             this.focus();
35212             this.addClass("x-tree-selected");
35213         }else{
35214             //this.blur();
35215             this.removeClass("x-tree-selected");
35216         }
35217     },
35218
35219     onMove : function(tree, node, oldParent, newParent, index, refNode){
35220         this.childIndent = null;
35221         if(this.rendered){
35222             var targetNode = newParent.ui.getContainer();
35223             if(!targetNode){//target not rendered
35224                 this.holder = document.createElement("div");
35225                 this.holder.appendChild(this.wrap);
35226                 return;
35227             }
35228             var insertBefore = refNode ? refNode.ui.getEl() : null;
35229             if(insertBefore){
35230                 targetNode.insertBefore(this.wrap, insertBefore);
35231             }else{
35232                 targetNode.appendChild(this.wrap);
35233             }
35234             this.node.renderIndent(true);
35235         }
35236     },
35237
35238     addClass : function(cls){
35239         if(this.elNode){
35240             Roo.fly(this.elNode).addClass(cls);
35241         }
35242     },
35243
35244     removeClass : function(cls){
35245         if(this.elNode){
35246             Roo.fly(this.elNode).removeClass(cls);
35247         }
35248     },
35249
35250     remove : function(){
35251         if(this.rendered){
35252             this.holder = document.createElement("div");
35253             this.holder.appendChild(this.wrap);
35254         }
35255     },
35256
35257     fireEvent : function(){
35258         return this.node.fireEvent.apply(this.node, arguments);
35259     },
35260
35261     initEvents : function(){
35262         this.node.on("move", this.onMove, this);
35263         var E = Roo.EventManager;
35264         var a = this.anchor;
35265
35266         var el = Roo.fly(a, '_treeui');
35267
35268         if(Roo.isOpera){ // opera render bug ignores the CSS
35269             el.setStyle("text-decoration", "none");
35270         }
35271
35272         el.on("click", this.onClick, this);
35273         el.on("dblclick", this.onDblClick, this);
35274
35275         if(this.checkbox){
35276             Roo.EventManager.on(this.checkbox,
35277                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35278         }
35279
35280         el.on("contextmenu", this.onContextMenu, this);
35281
35282         var icon = Roo.fly(this.iconNode);
35283         icon.on("click", this.onClick, this);
35284         icon.on("dblclick", this.onDblClick, this);
35285         icon.on("contextmenu", this.onContextMenu, this);
35286         E.on(this.ecNode, "click", this.ecClick, this, true);
35287
35288         if(this.node.disabled){
35289             this.addClass("x-tree-node-disabled");
35290         }
35291         if(this.node.hidden){
35292             this.addClass("x-tree-node-disabled");
35293         }
35294         var ot = this.node.getOwnerTree();
35295         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35296         if(dd && (!this.node.isRoot || ot.rootVisible)){
35297             Roo.dd.Registry.register(this.elNode, {
35298                 node: this.node,
35299                 handles: this.getDDHandles(),
35300                 isHandle: false
35301             });
35302         }
35303     },
35304
35305     getDDHandles : function(){
35306         return [this.iconNode, this.textNode];
35307     },
35308
35309     hide : function(){
35310         if(this.rendered){
35311             this.wrap.style.display = "none";
35312         }
35313     },
35314
35315     show : function(){
35316         if(this.rendered){
35317             this.wrap.style.display = "";
35318         }
35319     },
35320
35321     onContextMenu : function(e){
35322         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35323             e.preventDefault();
35324             this.focus();
35325             this.fireEvent("contextmenu", this.node, e);
35326         }
35327     },
35328
35329     onClick : function(e){
35330         if(this.dropping){
35331             e.stopEvent();
35332             return;
35333         }
35334         if(this.fireEvent("beforeclick", this.node, e) !== false){
35335             if(!this.disabled && this.node.attributes.href){
35336                 this.fireEvent("click", this.node, e);
35337                 return;
35338             }
35339             e.preventDefault();
35340             if(this.disabled){
35341                 return;
35342             }
35343
35344             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35345                 this.node.toggle();
35346             }
35347
35348             this.fireEvent("click", this.node, e);
35349         }else{
35350             e.stopEvent();
35351         }
35352     },
35353
35354     onDblClick : function(e){
35355         e.preventDefault();
35356         if(this.disabled){
35357             return;
35358         }
35359         if(this.checkbox){
35360             this.toggleCheck();
35361         }
35362         if(!this.animating && this.node.hasChildNodes()){
35363             this.node.toggle();
35364         }
35365         this.fireEvent("dblclick", this.node, e);
35366     },
35367
35368     onCheckChange : function(){
35369         var checked = this.checkbox.checked;
35370         this.node.attributes.checked = checked;
35371         this.fireEvent('checkchange', this.node, checked);
35372     },
35373
35374     ecClick : function(e){
35375         if(!this.animating && this.node.hasChildNodes()){
35376             this.node.toggle();
35377         }
35378     },
35379
35380     startDrop : function(){
35381         this.dropping = true;
35382     },
35383
35384     // delayed drop so the click event doesn't get fired on a drop
35385     endDrop : function(){
35386        setTimeout(function(){
35387            this.dropping = false;
35388        }.createDelegate(this), 50);
35389     },
35390
35391     expand : function(){
35392         this.updateExpandIcon();
35393         this.ctNode.style.display = "";
35394     },
35395
35396     focus : function(){
35397         if(!this.node.preventHScroll){
35398             try{this.anchor.focus();
35399             }catch(e){}
35400         }else if(!Roo.isIE){
35401             try{
35402                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35403                 var l = noscroll.scrollLeft;
35404                 this.anchor.focus();
35405                 noscroll.scrollLeft = l;
35406             }catch(e){}
35407         }
35408     },
35409
35410     toggleCheck : function(value){
35411         var cb = this.checkbox;
35412         if(cb){
35413             cb.checked = (value === undefined ? !cb.checked : value);
35414         }
35415     },
35416
35417     blur : function(){
35418         try{
35419             this.anchor.blur();
35420         }catch(e){}
35421     },
35422
35423     animExpand : function(callback){
35424         var ct = Roo.get(this.ctNode);
35425         ct.stopFx();
35426         if(!this.node.hasChildNodes()){
35427             this.updateExpandIcon();
35428             this.ctNode.style.display = "";
35429             Roo.callback(callback);
35430             return;
35431         }
35432         this.animating = true;
35433         this.updateExpandIcon();
35434
35435         ct.slideIn('t', {
35436            callback : function(){
35437                this.animating = false;
35438                Roo.callback(callback);
35439             },
35440             scope: this,
35441             duration: this.node.ownerTree.duration || .25
35442         });
35443     },
35444
35445     highlight : function(){
35446         var tree = this.node.getOwnerTree();
35447         Roo.fly(this.wrap).highlight(
35448             tree.hlColor || "C3DAF9",
35449             {endColor: tree.hlBaseColor}
35450         );
35451     },
35452
35453     collapse : function(){
35454         this.updateExpandIcon();
35455         this.ctNode.style.display = "none";
35456     },
35457
35458     animCollapse : function(callback){
35459         var ct = Roo.get(this.ctNode);
35460         ct.enableDisplayMode('block');
35461         ct.stopFx();
35462
35463         this.animating = true;
35464         this.updateExpandIcon();
35465
35466         ct.slideOut('t', {
35467             callback : function(){
35468                this.animating = false;
35469                Roo.callback(callback);
35470             },
35471             scope: this,
35472             duration: this.node.ownerTree.duration || .25
35473         });
35474     },
35475
35476     getContainer : function(){
35477         return this.ctNode;
35478     },
35479
35480     getEl : function(){
35481         return this.wrap;
35482     },
35483
35484     appendDDGhost : function(ghostNode){
35485         ghostNode.appendChild(this.elNode.cloneNode(true));
35486     },
35487
35488     getDDRepairXY : function(){
35489         return Roo.lib.Dom.getXY(this.iconNode);
35490     },
35491
35492     onRender : function(){
35493         this.render();
35494     },
35495
35496     render : function(bulkRender){
35497         var n = this.node, a = n.attributes;
35498         var targetNode = n.parentNode ?
35499               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35500
35501         if(!this.rendered){
35502             this.rendered = true;
35503
35504             this.renderElements(n, a, targetNode, bulkRender);
35505
35506             if(a.qtip){
35507                if(this.textNode.setAttributeNS){
35508                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35509                    if(a.qtipTitle){
35510                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35511                    }
35512                }else{
35513                    this.textNode.setAttribute("ext:qtip", a.qtip);
35514                    if(a.qtipTitle){
35515                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35516                    }
35517                }
35518             }else if(a.qtipCfg){
35519                 a.qtipCfg.target = Roo.id(this.textNode);
35520                 Roo.QuickTips.register(a.qtipCfg);
35521             }
35522             this.initEvents();
35523             if(!this.node.expanded){
35524                 this.updateExpandIcon();
35525             }
35526         }else{
35527             if(bulkRender === true) {
35528                 targetNode.appendChild(this.wrap);
35529             }
35530         }
35531     },
35532
35533     renderElements : function(n, a, targetNode, bulkRender)
35534     {
35535         // add some indent caching, this helps performance when rendering a large tree
35536         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35537         var t = n.getOwnerTree();
35538         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35539         if (typeof(n.attributes.html) != 'undefined') {
35540             txt = n.attributes.html;
35541         }
35542         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35543         var cb = typeof a.checked == 'boolean';
35544         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35545         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35546             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35547             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35548             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35549             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35550             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35551              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35552                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35553             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35554             "</li>"];
35555
35556         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35557             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35558                                 n.nextSibling.ui.getEl(), buf.join(""));
35559         }else{
35560             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35561         }
35562
35563         this.elNode = this.wrap.childNodes[0];
35564         this.ctNode = this.wrap.childNodes[1];
35565         var cs = this.elNode.childNodes;
35566         this.indentNode = cs[0];
35567         this.ecNode = cs[1];
35568         this.iconNode = cs[2];
35569         var index = 3;
35570         if(cb){
35571             this.checkbox = cs[3];
35572             index++;
35573         }
35574         this.anchor = cs[index];
35575         this.textNode = cs[index].firstChild;
35576     },
35577
35578     getAnchor : function(){
35579         return this.anchor;
35580     },
35581
35582     getTextEl : function(){
35583         return this.textNode;
35584     },
35585
35586     getIconEl : function(){
35587         return this.iconNode;
35588     },
35589
35590     isChecked : function(){
35591         return this.checkbox ? this.checkbox.checked : false;
35592     },
35593
35594     updateExpandIcon : function(){
35595         if(this.rendered){
35596             var n = this.node, c1, c2;
35597             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35598             var hasChild = n.hasChildNodes();
35599             if(hasChild){
35600                 if(n.expanded){
35601                     cls += "-minus";
35602                     c1 = "x-tree-node-collapsed";
35603                     c2 = "x-tree-node-expanded";
35604                 }else{
35605                     cls += "-plus";
35606                     c1 = "x-tree-node-expanded";
35607                     c2 = "x-tree-node-collapsed";
35608                 }
35609                 if(this.wasLeaf){
35610                     this.removeClass("x-tree-node-leaf");
35611                     this.wasLeaf = false;
35612                 }
35613                 if(this.c1 != c1 || this.c2 != c2){
35614                     Roo.fly(this.elNode).replaceClass(c1, c2);
35615                     this.c1 = c1; this.c2 = c2;
35616                 }
35617             }else{
35618                 // this changes non-leafs into leafs if they have no children.
35619                 // it's not very rational behaviour..
35620                 
35621                 if(!this.wasLeaf && this.node.leaf){
35622                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35623                     delete this.c1;
35624                     delete this.c2;
35625                     this.wasLeaf = true;
35626                 }
35627             }
35628             var ecc = "x-tree-ec-icon "+cls;
35629             if(this.ecc != ecc){
35630                 this.ecNode.className = ecc;
35631                 this.ecc = ecc;
35632             }
35633         }
35634     },
35635
35636     getChildIndent : function(){
35637         if(!this.childIndent){
35638             var buf = [];
35639             var p = this.node;
35640             while(p){
35641                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35642                     if(!p.isLast()) {
35643                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35644                     } else {
35645                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35646                     }
35647                 }
35648                 p = p.parentNode;
35649             }
35650             this.childIndent = buf.join("");
35651         }
35652         return this.childIndent;
35653     },
35654
35655     renderIndent : function(){
35656         if(this.rendered){
35657             var indent = "";
35658             var p = this.node.parentNode;
35659             if(p){
35660                 indent = p.ui.getChildIndent();
35661             }
35662             if(this.indentMarkup != indent){ // don't rerender if not required
35663                 this.indentNode.innerHTML = indent;
35664                 this.indentMarkup = indent;
35665             }
35666             this.updateExpandIcon();
35667         }
35668     }
35669 };
35670
35671 Roo.tree.RootTreeNodeUI = function(){
35672     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35673 };
35674 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35675     render : function(){
35676         if(!this.rendered){
35677             var targetNode = this.node.ownerTree.innerCt.dom;
35678             this.node.expanded = true;
35679             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35680             this.wrap = this.ctNode = targetNode.firstChild;
35681         }
35682     },
35683     collapse : function(){
35684     },
35685     expand : function(){
35686     }
35687 });/*
35688  * Based on:
35689  * Ext JS Library 1.1.1
35690  * Copyright(c) 2006-2007, Ext JS, LLC.
35691  *
35692  * Originally Released Under LGPL - original licence link has changed is not relivant.
35693  *
35694  * Fork - LGPL
35695  * <script type="text/javascript">
35696  */
35697 /**
35698  * @class Roo.tree.TreeLoader
35699  * @extends Roo.util.Observable
35700  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35701  * nodes from a specified URL. The response must be a javascript Array definition
35702  * who's elements are node definition objects. eg:
35703  * <pre><code>
35704 {  success : true,
35705    data :      [
35706    
35707     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35708     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35709     ]
35710 }
35711
35712
35713 </code></pre>
35714  * <br><br>
35715  * The old style respose with just an array is still supported, but not recommended.
35716  * <br><br>
35717  *
35718  * A server request is sent, and child nodes are loaded only when a node is expanded.
35719  * The loading node's id is passed to the server under the parameter name "node" to
35720  * enable the server to produce the correct child nodes.
35721  * <br><br>
35722  * To pass extra parameters, an event handler may be attached to the "beforeload"
35723  * event, and the parameters specified in the TreeLoader's baseParams property:
35724  * <pre><code>
35725     myTreeLoader.on("beforeload", function(treeLoader, node) {
35726         this.baseParams.category = node.attributes.category;
35727     }, this);
35728 </code></pre><
35729  * This would pass an HTTP parameter called "category" to the server containing
35730  * the value of the Node's "category" attribute.
35731  * @constructor
35732  * Creates a new Treeloader.
35733  * @param {Object} config A config object containing config properties.
35734  */
35735 Roo.tree.TreeLoader = function(config){
35736     this.baseParams = {};
35737     this.requestMethod = "POST";
35738     Roo.apply(this, config);
35739
35740     this.addEvents({
35741     
35742         /**
35743          * @event beforeload
35744          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35745          * @param {Object} This TreeLoader object.
35746          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35747          * @param {Object} callback The callback function specified in the {@link #load} call.
35748          */
35749         beforeload : true,
35750         /**
35751          * @event load
35752          * Fires when the node has been successfuly loaded.
35753          * @param {Object} This TreeLoader object.
35754          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35755          * @param {Object} response The response object containing the data from the server.
35756          */
35757         load : true,
35758         /**
35759          * @event loadexception
35760          * Fires if the network request failed.
35761          * @param {Object} This TreeLoader object.
35762          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35763          * @param {Object} response The response object containing the data from the server.
35764          */
35765         loadexception : true,
35766         /**
35767          * @event create
35768          * Fires before a node is created, enabling you to return custom Node types 
35769          * @param {Object} This TreeLoader object.
35770          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35771          */
35772         create : true
35773     });
35774
35775     Roo.tree.TreeLoader.superclass.constructor.call(this);
35776 };
35777
35778 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35779     /**
35780     * @cfg {String} dataUrl The URL from which to request a Json string which
35781     * specifies an array of node definition object representing the child nodes
35782     * to be loaded.
35783     */
35784     /**
35785     * @cfg {String} requestMethod either GET or POST
35786     * defaults to POST (due to BC)
35787     * to be loaded.
35788     */
35789     /**
35790     * @cfg {Object} baseParams (optional) An object containing properties which
35791     * specify HTTP parameters to be passed to each request for child nodes.
35792     */
35793     /**
35794     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35795     * created by this loader. If the attributes sent by the server have an attribute in this object,
35796     * they take priority.
35797     */
35798     /**
35799     * @cfg {Object} uiProviders (optional) An object containing properties which
35800     * 
35801     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35802     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35803     * <i>uiProvider</i> attribute of a returned child node is a string rather
35804     * than a reference to a TreeNodeUI implementation, this that string value
35805     * is used as a property name in the uiProviders object. You can define the provider named
35806     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35807     */
35808     uiProviders : {},
35809
35810     /**
35811     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35812     * child nodes before loading.
35813     */
35814     clearOnLoad : true,
35815
35816     /**
35817     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35818     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35819     * Grid query { data : [ .....] }
35820     */
35821     
35822     root : false,
35823      /**
35824     * @cfg {String} queryParam (optional) 
35825     * Name of the query as it will be passed on the querystring (defaults to 'node')
35826     * eg. the request will be ?node=[id]
35827     */
35828     
35829     
35830     queryParam: false,
35831     
35832     /**
35833      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35834      * This is called automatically when a node is expanded, but may be used to reload
35835      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35836      * @param {Roo.tree.TreeNode} node
35837      * @param {Function} callback
35838      */
35839     load : function(node, callback){
35840         if(this.clearOnLoad){
35841             while(node.firstChild){
35842                 node.removeChild(node.firstChild);
35843             }
35844         }
35845         if(node.attributes.children){ // preloaded json children
35846             var cs = node.attributes.children;
35847             for(var i = 0, len = cs.length; i < len; i++){
35848                 node.appendChild(this.createNode(cs[i]));
35849             }
35850             if(typeof callback == "function"){
35851                 callback();
35852             }
35853         }else if(this.dataUrl){
35854             this.requestData(node, callback);
35855         }
35856     },
35857
35858     getParams: function(node){
35859         var buf = [], bp = this.baseParams;
35860         for(var key in bp){
35861             if(typeof bp[key] != "function"){
35862                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
35863             }
35864         }
35865         var n = this.queryParam === false ? 'node' : this.queryParam;
35866         buf.push(n + "=", encodeURIComponent(node.id));
35867         return buf.join("");
35868     },
35869
35870     requestData : function(node, callback){
35871         if(this.fireEvent("beforeload", this, node, callback) !== false){
35872             this.transId = Roo.Ajax.request({
35873                 method:this.requestMethod,
35874                 url: this.dataUrl||this.url,
35875                 success: this.handleResponse,
35876                 failure: this.handleFailure,
35877                 scope: this,
35878                 argument: {callback: callback, node: node},
35879                 params: this.getParams(node)
35880             });
35881         }else{
35882             // if the load is cancelled, make sure we notify
35883             // the node that we are done
35884             if(typeof callback == "function"){
35885                 callback();
35886             }
35887         }
35888     },
35889
35890     isLoading : function(){
35891         return this.transId ? true : false;
35892     },
35893
35894     abort : function(){
35895         if(this.isLoading()){
35896             Roo.Ajax.abort(this.transId);
35897         }
35898     },
35899
35900     // private
35901     createNode : function(attr)
35902     {
35903         // apply baseAttrs, nice idea Corey!
35904         if(this.baseAttrs){
35905             Roo.applyIf(attr, this.baseAttrs);
35906         }
35907         if(this.applyLoader !== false){
35908             attr.loader = this;
35909         }
35910         // uiProvider = depreciated..
35911         
35912         if(typeof(attr.uiProvider) == 'string'){
35913            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
35914                 /**  eval:var:attr */ eval(attr.uiProvider);
35915         }
35916         if(typeof(this.uiProviders['default']) != 'undefined') {
35917             attr.uiProvider = this.uiProviders['default'];
35918         }
35919         
35920         this.fireEvent('create', this, attr);
35921         
35922         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
35923         return(attr.leaf ?
35924                         new Roo.tree.TreeNode(attr) :
35925                         new Roo.tree.AsyncTreeNode(attr));
35926     },
35927
35928     processResponse : function(response, node, callback)
35929     {
35930         var json = response.responseText;
35931         try {
35932             
35933             var o = Roo.decode(json);
35934             
35935             if (this.root === false && typeof(o.success) != undefined) {
35936                 this.root = 'data'; // the default behaviour for list like data..
35937                 }
35938                 
35939             if (this.root !== false &&  !o.success) {
35940                 // it's a failure condition.
35941                 var a = response.argument;
35942                 this.fireEvent("loadexception", this, a.node, response);
35943                 Roo.log("Load failed - should have a handler really");
35944                 return;
35945             }
35946             
35947             
35948             
35949             if (this.root !== false) {
35950                  o = o[this.root];
35951             }
35952             
35953             for(var i = 0, len = o.length; i < len; i++){
35954                 var n = this.createNode(o[i]);
35955                 if(n){
35956                     node.appendChild(n);
35957                 }
35958             }
35959             if(typeof callback == "function"){
35960                 callback(this, node);
35961             }
35962         }catch(e){
35963             this.handleFailure(response);
35964         }
35965     },
35966
35967     handleResponse : function(response){
35968         this.transId = false;
35969         var a = response.argument;
35970         this.processResponse(response, a.node, a.callback);
35971         this.fireEvent("load", this, a.node, response);
35972     },
35973
35974     handleFailure : function(response)
35975     {
35976         // should handle failure better..
35977         this.transId = false;
35978         var a = response.argument;
35979         this.fireEvent("loadexception", this, a.node, response);
35980         if(typeof a.callback == "function"){
35981             a.callback(this, a.node);
35982         }
35983     }
35984 });/*
35985  * Based on:
35986  * Ext JS Library 1.1.1
35987  * Copyright(c) 2006-2007, Ext JS, LLC.
35988  *
35989  * Originally Released Under LGPL - original licence link has changed is not relivant.
35990  *
35991  * Fork - LGPL
35992  * <script type="text/javascript">
35993  */
35994
35995 /**
35996 * @class Roo.tree.TreeFilter
35997 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
35998 * @param {TreePanel} tree
35999 * @param {Object} config (optional)
36000  */
36001 Roo.tree.TreeFilter = function(tree, config){
36002     this.tree = tree;
36003     this.filtered = {};
36004     Roo.apply(this, config);
36005 };
36006
36007 Roo.tree.TreeFilter.prototype = {
36008     clearBlank:false,
36009     reverse:false,
36010     autoClear:false,
36011     remove:false,
36012
36013      /**
36014      * Filter the data by a specific attribute.
36015      * @param {String/RegExp} value Either string that the attribute value
36016      * should start with or a RegExp to test against the attribute
36017      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36018      * @param {TreeNode} startNode (optional) The node to start the filter at.
36019      */
36020     filter : function(value, attr, startNode){
36021         attr = attr || "text";
36022         var f;
36023         if(typeof value == "string"){
36024             var vlen = value.length;
36025             // auto clear empty filter
36026             if(vlen == 0 && this.clearBlank){
36027                 this.clear();
36028                 return;
36029             }
36030             value = value.toLowerCase();
36031             f = function(n){
36032                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36033             };
36034         }else if(value.exec){ // regex?
36035             f = function(n){
36036                 return value.test(n.attributes[attr]);
36037             };
36038         }else{
36039             throw 'Illegal filter type, must be string or regex';
36040         }
36041         this.filterBy(f, null, startNode);
36042         },
36043
36044     /**
36045      * Filter by a function. The passed function will be called with each
36046      * node in the tree (or from the startNode). If the function returns true, the node is kept
36047      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36048      * @param {Function} fn The filter function
36049      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36050      */
36051     filterBy : function(fn, scope, startNode){
36052         startNode = startNode || this.tree.root;
36053         if(this.autoClear){
36054             this.clear();
36055         }
36056         var af = this.filtered, rv = this.reverse;
36057         var f = function(n){
36058             if(n == startNode){
36059                 return true;
36060             }
36061             if(af[n.id]){
36062                 return false;
36063             }
36064             var m = fn.call(scope || n, n);
36065             if(!m || rv){
36066                 af[n.id] = n;
36067                 n.ui.hide();
36068                 return false;
36069             }
36070             return true;
36071         };
36072         startNode.cascade(f);
36073         if(this.remove){
36074            for(var id in af){
36075                if(typeof id != "function"){
36076                    var n = af[id];
36077                    if(n && n.parentNode){
36078                        n.parentNode.removeChild(n);
36079                    }
36080                }
36081            }
36082         }
36083     },
36084
36085     /**
36086      * Clears the current filter. Note: with the "remove" option
36087      * set a filter cannot be cleared.
36088      */
36089     clear : function(){
36090         var t = this.tree;
36091         var af = this.filtered;
36092         for(var id in af){
36093             if(typeof id != "function"){
36094                 var n = af[id];
36095                 if(n){
36096                     n.ui.show();
36097                 }
36098             }
36099         }
36100         this.filtered = {};
36101     }
36102 };
36103 /*
36104  * Based on:
36105  * Ext JS Library 1.1.1
36106  * Copyright(c) 2006-2007, Ext JS, LLC.
36107  *
36108  * Originally Released Under LGPL - original licence link has changed is not relivant.
36109  *
36110  * Fork - LGPL
36111  * <script type="text/javascript">
36112  */
36113  
36114
36115 /**
36116  * @class Roo.tree.TreeSorter
36117  * Provides sorting of nodes in a TreePanel
36118  * 
36119  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36120  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36121  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36122  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36123  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36124  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36125  * @constructor
36126  * @param {TreePanel} tree
36127  * @param {Object} config
36128  */
36129 Roo.tree.TreeSorter = function(tree, config){
36130     Roo.apply(this, config);
36131     tree.on("beforechildrenrendered", this.doSort, this);
36132     tree.on("append", this.updateSort, this);
36133     tree.on("insert", this.updateSort, this);
36134     
36135     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36136     var p = this.property || "text";
36137     var sortType = this.sortType;
36138     var fs = this.folderSort;
36139     var cs = this.caseSensitive === true;
36140     var leafAttr = this.leafAttr || 'leaf';
36141
36142     this.sortFn = function(n1, n2){
36143         if(fs){
36144             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36145                 return 1;
36146             }
36147             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36148                 return -1;
36149             }
36150         }
36151         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36152         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36153         if(v1 < v2){
36154                         return dsc ? +1 : -1;
36155                 }else if(v1 > v2){
36156                         return dsc ? -1 : +1;
36157         }else{
36158                 return 0;
36159         }
36160     };
36161 };
36162
36163 Roo.tree.TreeSorter.prototype = {
36164     doSort : function(node){
36165         node.sort(this.sortFn);
36166     },
36167     
36168     compareNodes : function(n1, n2){
36169         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36170     },
36171     
36172     updateSort : function(tree, node){
36173         if(node.childrenRendered){
36174             this.doSort.defer(1, this, [node]);
36175         }
36176     }
36177 };/*
36178  * Based on:
36179  * Ext JS Library 1.1.1
36180  * Copyright(c) 2006-2007, Ext JS, LLC.
36181  *
36182  * Originally Released Under LGPL - original licence link has changed is not relivant.
36183  *
36184  * Fork - LGPL
36185  * <script type="text/javascript">
36186  */
36187
36188 if(Roo.dd.DropZone){
36189     
36190 Roo.tree.TreeDropZone = function(tree, config){
36191     this.allowParentInsert = false;
36192     this.allowContainerDrop = false;
36193     this.appendOnly = false;
36194     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36195     this.tree = tree;
36196     this.lastInsertClass = "x-tree-no-status";
36197     this.dragOverData = {};
36198 };
36199
36200 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36201     ddGroup : "TreeDD",
36202     scroll:  true,
36203     
36204     expandDelay : 1000,
36205     
36206     expandNode : function(node){
36207         if(node.hasChildNodes() && !node.isExpanded()){
36208             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36209         }
36210     },
36211     
36212     queueExpand : function(node){
36213         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36214     },
36215     
36216     cancelExpand : function(){
36217         if(this.expandProcId){
36218             clearTimeout(this.expandProcId);
36219             this.expandProcId = false;
36220         }
36221     },
36222     
36223     isValidDropPoint : function(n, pt, dd, e, data){
36224         if(!n || !data){ return false; }
36225         var targetNode = n.node;
36226         var dropNode = data.node;
36227         // default drop rules
36228         if(!(targetNode && targetNode.isTarget && pt)){
36229             return false;
36230         }
36231         if(pt == "append" && targetNode.allowChildren === false){
36232             return false;
36233         }
36234         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36235             return false;
36236         }
36237         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36238             return false;
36239         }
36240         // reuse the object
36241         var overEvent = this.dragOverData;
36242         overEvent.tree = this.tree;
36243         overEvent.target = targetNode;
36244         overEvent.data = data;
36245         overEvent.point = pt;
36246         overEvent.source = dd;
36247         overEvent.rawEvent = e;
36248         overEvent.dropNode = dropNode;
36249         overEvent.cancel = false;  
36250         var result = this.tree.fireEvent("nodedragover", overEvent);
36251         return overEvent.cancel === false && result !== false;
36252     },
36253     
36254     getDropPoint : function(e, n, dd)
36255     {
36256         var tn = n.node;
36257         if(tn.isRoot){
36258             return tn.allowChildren !== false ? "append" : false; // always append for root
36259         }
36260         var dragEl = n.ddel;
36261         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36262         var y = Roo.lib.Event.getPageY(e);
36263         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36264         
36265         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36266         var noAppend = tn.allowChildren === false;
36267         if(this.appendOnly || tn.parentNode.allowChildren === false){
36268             return noAppend ? false : "append";
36269         }
36270         var noBelow = false;
36271         if(!this.allowParentInsert){
36272             noBelow = tn.hasChildNodes() && tn.isExpanded();
36273         }
36274         var q = (b - t) / (noAppend ? 2 : 3);
36275         if(y >= t && y < (t + q)){
36276             return "above";
36277         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36278             return "below";
36279         }else{
36280             return "append";
36281         }
36282     },
36283     
36284     onNodeEnter : function(n, dd, e, data)
36285     {
36286         this.cancelExpand();
36287     },
36288     
36289     onNodeOver : function(n, dd, e, data)
36290     {
36291        
36292         var pt = this.getDropPoint(e, n, dd);
36293         var node = n.node;
36294         
36295         // auto node expand check
36296         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36297             this.queueExpand(node);
36298         }else if(pt != "append"){
36299             this.cancelExpand();
36300         }
36301         
36302         // set the insert point style on the target node
36303         var returnCls = this.dropNotAllowed;
36304         if(this.isValidDropPoint(n, pt, dd, e, data)){
36305            if(pt){
36306                var el = n.ddel;
36307                var cls;
36308                if(pt == "above"){
36309                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36310                    cls = "x-tree-drag-insert-above";
36311                }else if(pt == "below"){
36312                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36313                    cls = "x-tree-drag-insert-below";
36314                }else{
36315                    returnCls = "x-tree-drop-ok-append";
36316                    cls = "x-tree-drag-append";
36317                }
36318                if(this.lastInsertClass != cls){
36319                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36320                    this.lastInsertClass = cls;
36321                }
36322            }
36323        }
36324        return returnCls;
36325     },
36326     
36327     onNodeOut : function(n, dd, e, data){
36328         
36329         this.cancelExpand();
36330         this.removeDropIndicators(n);
36331     },
36332     
36333     onNodeDrop : function(n, dd, e, data){
36334         var point = this.getDropPoint(e, n, dd);
36335         var targetNode = n.node;
36336         targetNode.ui.startDrop();
36337         if(!this.isValidDropPoint(n, point, dd, e, data)){
36338             targetNode.ui.endDrop();
36339             return false;
36340         }
36341         // first try to find the drop node
36342         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36343         var dropEvent = {
36344             tree : this.tree,
36345             target: targetNode,
36346             data: data,
36347             point: point,
36348             source: dd,
36349             rawEvent: e,
36350             dropNode: dropNode,
36351             cancel: !dropNode   
36352         };
36353         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36354         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36355             targetNode.ui.endDrop();
36356             return false;
36357         }
36358         // allow target changing
36359         targetNode = dropEvent.target;
36360         if(point == "append" && !targetNode.isExpanded()){
36361             targetNode.expand(false, null, function(){
36362                 this.completeDrop(dropEvent);
36363             }.createDelegate(this));
36364         }else{
36365             this.completeDrop(dropEvent);
36366         }
36367         return true;
36368     },
36369     
36370     completeDrop : function(de){
36371         var ns = de.dropNode, p = de.point, t = de.target;
36372         if(!(ns instanceof Array)){
36373             ns = [ns];
36374         }
36375         var n;
36376         for(var i = 0, len = ns.length; i < len; i++){
36377             n = ns[i];
36378             if(p == "above"){
36379                 t.parentNode.insertBefore(n, t);
36380             }else if(p == "below"){
36381                 t.parentNode.insertBefore(n, t.nextSibling);
36382             }else{
36383                 t.appendChild(n);
36384             }
36385         }
36386         n.ui.focus();
36387         if(this.tree.hlDrop){
36388             n.ui.highlight();
36389         }
36390         t.ui.endDrop();
36391         this.tree.fireEvent("nodedrop", de);
36392     },
36393     
36394     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36395         if(this.tree.hlDrop){
36396             dropNode.ui.focus();
36397             dropNode.ui.highlight();
36398         }
36399         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36400     },
36401     
36402     getTree : function(){
36403         return this.tree;
36404     },
36405     
36406     removeDropIndicators : function(n){
36407         if(n && n.ddel){
36408             var el = n.ddel;
36409             Roo.fly(el).removeClass([
36410                     "x-tree-drag-insert-above",
36411                     "x-tree-drag-insert-below",
36412                     "x-tree-drag-append"]);
36413             this.lastInsertClass = "_noclass";
36414         }
36415     },
36416     
36417     beforeDragDrop : function(target, e, id){
36418         this.cancelExpand();
36419         return true;
36420     },
36421     
36422     afterRepair : function(data){
36423         if(data && Roo.enableFx){
36424             data.node.ui.highlight();
36425         }
36426         this.hideProxy();
36427     } 
36428     
36429 });
36430
36431 }
36432 /*
36433  * Based on:
36434  * Ext JS Library 1.1.1
36435  * Copyright(c) 2006-2007, Ext JS, LLC.
36436  *
36437  * Originally Released Under LGPL - original licence link has changed is not relivant.
36438  *
36439  * Fork - LGPL
36440  * <script type="text/javascript">
36441  */
36442  
36443
36444 if(Roo.dd.DragZone){
36445 Roo.tree.TreeDragZone = function(tree, config){
36446     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36447     this.tree = tree;
36448 };
36449
36450 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36451     ddGroup : "TreeDD",
36452    
36453     onBeforeDrag : function(data, e){
36454         var n = data.node;
36455         return n && n.draggable && !n.disabled;
36456     },
36457      
36458     
36459     onInitDrag : function(e){
36460         var data = this.dragData;
36461         this.tree.getSelectionModel().select(data.node);
36462         this.proxy.update("");
36463         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36464         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36465     },
36466     
36467     getRepairXY : function(e, data){
36468         return data.node.ui.getDDRepairXY();
36469     },
36470     
36471     onEndDrag : function(data, e){
36472         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36473         
36474         
36475     },
36476     
36477     onValidDrop : function(dd, e, id){
36478         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36479         this.hideProxy();
36480     },
36481     
36482     beforeInvalidDrop : function(e, id){
36483         // this scrolls the original position back into view
36484         var sm = this.tree.getSelectionModel();
36485         sm.clearSelections();
36486         sm.select(this.dragData.node);
36487     }
36488 });
36489 }/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499 /**
36500  * @class Roo.tree.TreeEditor
36501  * @extends Roo.Editor
36502  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36503  * as the editor field.
36504  * @constructor
36505  * @param {Object} config (used to be the tree panel.)
36506  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36507  * 
36508  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36509  * @cfg {Roo.form.TextField|Object} field The field configuration
36510  *
36511  * 
36512  */
36513 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36514     var tree = config;
36515     var field;
36516     if (oldconfig) { // old style..
36517         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36518     } else {
36519         // new style..
36520         tree = config.tree;
36521         config.field = config.field  || {};
36522         config.field.xtype = 'TextField';
36523         field = Roo.factory(config.field, Roo.form);
36524     }
36525     config = config || {};
36526     
36527     
36528     this.addEvents({
36529         /**
36530          * @event beforenodeedit
36531          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36532          * false from the handler of this event.
36533          * @param {Editor} this
36534          * @param {Roo.tree.Node} node 
36535          */
36536         "beforenodeedit" : true
36537     });
36538     
36539     //Roo.log(config);
36540     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36541
36542     this.tree = tree;
36543
36544     tree.on('beforeclick', this.beforeNodeClick, this);
36545     tree.getTreeEl().on('mousedown', this.hide, this);
36546     this.on('complete', this.updateNode, this);
36547     this.on('beforestartedit', this.fitToTree, this);
36548     this.on('startedit', this.bindScroll, this, {delay:10});
36549     this.on('specialkey', this.onSpecialKey, this);
36550 };
36551
36552 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36553     /**
36554      * @cfg {String} alignment
36555      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36556      */
36557     alignment: "l-l",
36558     // inherit
36559     autoSize: false,
36560     /**
36561      * @cfg {Boolean} hideEl
36562      * True to hide the bound element while the editor is displayed (defaults to false)
36563      */
36564     hideEl : false,
36565     /**
36566      * @cfg {String} cls
36567      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36568      */
36569     cls: "x-small-editor x-tree-editor",
36570     /**
36571      * @cfg {Boolean} shim
36572      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36573      */
36574     shim:false,
36575     // inherit
36576     shadow:"frame",
36577     /**
36578      * @cfg {Number} maxWidth
36579      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36580      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36581      * scroll and client offsets into account prior to each edit.
36582      */
36583     maxWidth: 250,
36584
36585     editDelay : 350,
36586
36587     // private
36588     fitToTree : function(ed, el){
36589         var td = this.tree.getTreeEl().dom, nd = el.dom;
36590         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36591             td.scrollLeft = nd.offsetLeft;
36592         }
36593         var w = Math.min(
36594                 this.maxWidth,
36595                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36596         this.setSize(w, '');
36597         
36598         return this.fireEvent('beforenodeedit', this, this.editNode);
36599         
36600     },
36601
36602     // private
36603     triggerEdit : function(node){
36604         this.completeEdit();
36605         this.editNode = node;
36606         this.startEdit(node.ui.textNode, node.text);
36607     },
36608
36609     // private
36610     bindScroll : function(){
36611         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36612     },
36613
36614     // private
36615     beforeNodeClick : function(node, e){
36616         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36617         this.lastClick = new Date();
36618         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36619             e.stopEvent();
36620             this.triggerEdit(node);
36621             return false;
36622         }
36623         return true;
36624     },
36625
36626     // private
36627     updateNode : function(ed, value){
36628         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36629         this.editNode.setText(value);
36630     },
36631
36632     // private
36633     onHide : function(){
36634         Roo.tree.TreeEditor.superclass.onHide.call(this);
36635         if(this.editNode){
36636             this.editNode.ui.focus();
36637         }
36638     },
36639
36640     // private
36641     onSpecialKey : function(field, e){
36642         var k = e.getKey();
36643         if(k == e.ESC){
36644             e.stopEvent();
36645             this.cancelEdit();
36646         }else if(k == e.ENTER && !e.hasModifier()){
36647             e.stopEvent();
36648             this.completeEdit();
36649         }
36650     }
36651 });//<Script type="text/javascript">
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 /**
36664  * Not documented??? - probably should be...
36665  */
36666
36667 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36668     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36669     
36670     renderElements : function(n, a, targetNode, bulkRender){
36671         //consel.log("renderElements?");
36672         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36673
36674         var t = n.getOwnerTree();
36675         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36676         
36677         var cols = t.columns;
36678         var bw = t.borderWidth;
36679         var c = cols[0];
36680         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36681          var cb = typeof a.checked == "boolean";
36682         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36683         var colcls = 'x-t-' + tid + '-c0';
36684         var buf = [
36685             '<li class="x-tree-node">',
36686             
36687                 
36688                 '<div class="x-tree-node-el ', a.cls,'">',
36689                     // extran...
36690                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36691                 
36692                 
36693                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36694                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36695                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36696                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36697                            (a.iconCls ? ' '+a.iconCls : ''),
36698                            '" unselectable="on" />',
36699                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36700                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36701                              
36702                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36703                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36704                             '<span unselectable="on" qtip="' + tx + '">',
36705                              tx,
36706                              '</span></a>' ,
36707                     '</div>',
36708                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36709                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36710                  ];
36711         for(var i = 1, len = cols.length; i < len; i++){
36712             c = cols[i];
36713             colcls = 'x-t-' + tid + '-c' +i;
36714             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36715             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36716                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36717                       "</div>");
36718          }
36719          
36720          buf.push(
36721             '</a>',
36722             '<div class="x-clear"></div></div>',
36723             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36724             "</li>");
36725         
36726         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36727             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36728                                 n.nextSibling.ui.getEl(), buf.join(""));
36729         }else{
36730             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36731         }
36732         var el = this.wrap.firstChild;
36733         this.elRow = el;
36734         this.elNode = el.firstChild;
36735         this.ranchor = el.childNodes[1];
36736         this.ctNode = this.wrap.childNodes[1];
36737         var cs = el.firstChild.childNodes;
36738         this.indentNode = cs[0];
36739         this.ecNode = cs[1];
36740         this.iconNode = cs[2];
36741         var index = 3;
36742         if(cb){
36743             this.checkbox = cs[3];
36744             index++;
36745         }
36746         this.anchor = cs[index];
36747         
36748         this.textNode = cs[index].firstChild;
36749         
36750         //el.on("click", this.onClick, this);
36751         //el.on("dblclick", this.onDblClick, this);
36752         
36753         
36754        // console.log(this);
36755     },
36756     initEvents : function(){
36757         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36758         
36759             
36760         var a = this.ranchor;
36761
36762         var el = Roo.get(a);
36763
36764         if(Roo.isOpera){ // opera render bug ignores the CSS
36765             el.setStyle("text-decoration", "none");
36766         }
36767
36768         el.on("click", this.onClick, this);
36769         el.on("dblclick", this.onDblClick, this);
36770         el.on("contextmenu", this.onContextMenu, this);
36771         
36772     },
36773     
36774     /*onSelectedChange : function(state){
36775         if(state){
36776             this.focus();
36777             this.addClass("x-tree-selected");
36778         }else{
36779             //this.blur();
36780             this.removeClass("x-tree-selected");
36781         }
36782     },*/
36783     addClass : function(cls){
36784         if(this.elRow){
36785             Roo.fly(this.elRow).addClass(cls);
36786         }
36787         
36788     },
36789     
36790     
36791     removeClass : function(cls){
36792         if(this.elRow){
36793             Roo.fly(this.elRow).removeClass(cls);
36794         }
36795     }
36796
36797     
36798     
36799 });//<Script type="text/javascript">
36800
36801 /*
36802  * Based on:
36803  * Ext JS Library 1.1.1
36804  * Copyright(c) 2006-2007, Ext JS, LLC.
36805  *
36806  * Originally Released Under LGPL - original licence link has changed is not relivant.
36807  *
36808  * Fork - LGPL
36809  * <script type="text/javascript">
36810  */
36811  
36812
36813 /**
36814  * @class Roo.tree.ColumnTree
36815  * @extends Roo.data.TreePanel
36816  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36817  * @cfg {int} borderWidth  compined right/left border allowance
36818  * @constructor
36819  * @param {String/HTMLElement/Element} el The container element
36820  * @param {Object} config
36821  */
36822 Roo.tree.ColumnTree =  function(el, config)
36823 {
36824    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36825    this.addEvents({
36826         /**
36827         * @event resize
36828         * Fire this event on a container when it resizes
36829         * @param {int} w Width
36830         * @param {int} h Height
36831         */
36832        "resize" : true
36833     });
36834     this.on('resize', this.onResize, this);
36835 };
36836
36837 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36838     //lines:false,
36839     
36840     
36841     borderWidth: Roo.isBorderBox ? 0 : 2, 
36842     headEls : false,
36843     
36844     render : function(){
36845         // add the header.....
36846        
36847         Roo.tree.ColumnTree.superclass.render.apply(this);
36848         
36849         this.el.addClass('x-column-tree');
36850         
36851         this.headers = this.el.createChild(
36852             {cls:'x-tree-headers'},this.innerCt.dom);
36853    
36854         var cols = this.columns, c;
36855         var totalWidth = 0;
36856         this.headEls = [];
36857         var  len = cols.length;
36858         for(var i = 0; i < len; i++){
36859              c = cols[i];
36860              totalWidth += c.width;
36861             this.headEls.push(this.headers.createChild({
36862                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
36863                  cn: {
36864                      cls:'x-tree-hd-text',
36865                      html: c.header
36866                  },
36867                  style:'width:'+(c.width-this.borderWidth)+'px;'
36868              }));
36869         }
36870         this.headers.createChild({cls:'x-clear'});
36871         // prevent floats from wrapping when clipped
36872         this.headers.setWidth(totalWidth);
36873         //this.innerCt.setWidth(totalWidth);
36874         this.innerCt.setStyle({ overflow: 'auto' });
36875         this.onResize(this.width, this.height);
36876              
36877         
36878     },
36879     onResize : function(w,h)
36880     {
36881         this.height = h;
36882         this.width = w;
36883         // resize cols..
36884         this.innerCt.setWidth(this.width);
36885         this.innerCt.setHeight(this.height-20);
36886         
36887         // headers...
36888         var cols = this.columns, c;
36889         var totalWidth = 0;
36890         var expEl = false;
36891         var len = cols.length;
36892         for(var i = 0; i < len; i++){
36893             c = cols[i];
36894             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
36895                 // it's the expander..
36896                 expEl  = this.headEls[i];
36897                 continue;
36898             }
36899             totalWidth += c.width;
36900             
36901         }
36902         if (expEl) {
36903             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
36904         }
36905         this.headers.setWidth(w-20);
36906
36907         
36908         
36909         
36910     }
36911 });
36912 /*
36913  * Based on:
36914  * Ext JS Library 1.1.1
36915  * Copyright(c) 2006-2007, Ext JS, LLC.
36916  *
36917  * Originally Released Under LGPL - original licence link has changed is not relivant.
36918  *
36919  * Fork - LGPL
36920  * <script type="text/javascript">
36921  */
36922  
36923 /**
36924  * @class Roo.menu.Menu
36925  * @extends Roo.util.Observable
36926  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
36927  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
36928  * @constructor
36929  * Creates a new Menu
36930  * @param {Object} config Configuration options
36931  */
36932 Roo.menu.Menu = function(config){
36933     Roo.apply(this, config);
36934     this.id = this.id || Roo.id();
36935     this.addEvents({
36936         /**
36937          * @event beforeshow
36938          * Fires before this menu is displayed
36939          * @param {Roo.menu.Menu} this
36940          */
36941         beforeshow : true,
36942         /**
36943          * @event beforehide
36944          * Fires before this menu is hidden
36945          * @param {Roo.menu.Menu} this
36946          */
36947         beforehide : true,
36948         /**
36949          * @event show
36950          * Fires after this menu is displayed
36951          * @param {Roo.menu.Menu} this
36952          */
36953         show : true,
36954         /**
36955          * @event hide
36956          * Fires after this menu is hidden
36957          * @param {Roo.menu.Menu} this
36958          */
36959         hide : true,
36960         /**
36961          * @event click
36962          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
36963          * @param {Roo.menu.Menu} this
36964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36965          * @param {Roo.EventObject} e
36966          */
36967         click : true,
36968         /**
36969          * @event mouseover
36970          * Fires when the mouse is hovering over this menu
36971          * @param {Roo.menu.Menu} this
36972          * @param {Roo.EventObject} e
36973          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36974          */
36975         mouseover : true,
36976         /**
36977          * @event mouseout
36978          * Fires when the mouse exits this menu
36979          * @param {Roo.menu.Menu} this
36980          * @param {Roo.EventObject} e
36981          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36982          */
36983         mouseout : true,
36984         /**
36985          * @event itemclick
36986          * Fires when a menu item contained in this menu is clicked
36987          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
36988          * @param {Roo.EventObject} e
36989          */
36990         itemclick: true
36991     });
36992     if (this.registerMenu) {
36993         Roo.menu.MenuMgr.register(this);
36994     }
36995     
36996     var mis = this.items;
36997     this.items = new Roo.util.MixedCollection();
36998     if(mis){
36999         this.add.apply(this, mis);
37000     }
37001 };
37002
37003 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37004     /**
37005      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37006      */
37007     minWidth : 120,
37008     /**
37009      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37010      * for bottom-right shadow (defaults to "sides")
37011      */
37012     shadow : "sides",
37013     /**
37014      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37015      * this menu (defaults to "tl-tr?")
37016      */
37017     subMenuAlign : "tl-tr?",
37018     /**
37019      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37020      * relative to its element of origin (defaults to "tl-bl?")
37021      */
37022     defaultAlign : "tl-bl?",
37023     /**
37024      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37025      */
37026     allowOtherMenus : false,
37027     /**
37028      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37029      */
37030     registerMenu : true,
37031
37032     hidden:true,
37033
37034     // private
37035     render : function(){
37036         if(this.el){
37037             return;
37038         }
37039         var el = this.el = new Roo.Layer({
37040             cls: "x-menu",
37041             shadow:this.shadow,
37042             constrain: false,
37043             parentEl: this.parentEl || document.body,
37044             zindex:15000
37045         });
37046
37047         this.keyNav = new Roo.menu.MenuNav(this);
37048
37049         if(this.plain){
37050             el.addClass("x-menu-plain");
37051         }
37052         if(this.cls){
37053             el.addClass(this.cls);
37054         }
37055         // generic focus element
37056         this.focusEl = el.createChild({
37057             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37058         });
37059         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37060         //disabling touch- as it's causing issues ..
37061         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37062         ul.on('click'   , this.onClick, this);
37063         
37064         
37065         ul.on("mouseover", this.onMouseOver, this);
37066         ul.on("mouseout", this.onMouseOut, this);
37067         this.items.each(function(item){
37068             if (item.hidden) {
37069                 return;
37070             }
37071             
37072             var li = document.createElement("li");
37073             li.className = "x-menu-list-item";
37074             ul.dom.appendChild(li);
37075             item.render(li, this);
37076         }, this);
37077         this.ul = ul;
37078         this.autoWidth();
37079     },
37080
37081     // private
37082     autoWidth : function(){
37083         var el = this.el, ul = this.ul;
37084         if(!el){
37085             return;
37086         }
37087         var w = this.width;
37088         if(w){
37089             el.setWidth(w);
37090         }else if(Roo.isIE){
37091             el.setWidth(this.minWidth);
37092             var t = el.dom.offsetWidth; // force recalc
37093             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37094         }
37095     },
37096
37097     // private
37098     delayAutoWidth : function(){
37099         if(this.rendered){
37100             if(!this.awTask){
37101                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37102             }
37103             this.awTask.delay(20);
37104         }
37105     },
37106
37107     // private
37108     findTargetItem : function(e){
37109         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37110         if(t && t.menuItemId){
37111             return this.items.get(t.menuItemId);
37112         }
37113     },
37114
37115     // private
37116     onClick : function(e){
37117         Roo.log("menu.onClick");
37118         var t = this.findTargetItem(e);
37119         if(!t){
37120             return;
37121         }
37122         Roo.log(e);
37123         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37124             if(t == this.activeItem && t.shouldDeactivate(e)){
37125                 this.activeItem.deactivate();
37126                 delete this.activeItem;
37127                 return;
37128             }
37129             if(t.canActivate){
37130                 this.setActiveItem(t, true);
37131             }
37132             return;
37133             
37134             
37135         }
37136         
37137         t.onClick(e);
37138         this.fireEvent("click", this, t, e);
37139     },
37140
37141     // private
37142     setActiveItem : function(item, autoExpand){
37143         if(item != this.activeItem){
37144             if(this.activeItem){
37145                 this.activeItem.deactivate();
37146             }
37147             this.activeItem = item;
37148             item.activate(autoExpand);
37149         }else if(autoExpand){
37150             item.expandMenu();
37151         }
37152     },
37153
37154     // private
37155     tryActivate : function(start, step){
37156         var items = this.items;
37157         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37158             var item = items.get(i);
37159             if(!item.disabled && item.canActivate){
37160                 this.setActiveItem(item, false);
37161                 return item;
37162             }
37163         }
37164         return false;
37165     },
37166
37167     // private
37168     onMouseOver : function(e){
37169         var t;
37170         if(t = this.findTargetItem(e)){
37171             if(t.canActivate && !t.disabled){
37172                 this.setActiveItem(t, true);
37173             }
37174         }
37175         this.fireEvent("mouseover", this, e, t);
37176     },
37177
37178     // private
37179     onMouseOut : function(e){
37180         var t;
37181         if(t = this.findTargetItem(e)){
37182             if(t == this.activeItem && t.shouldDeactivate(e)){
37183                 this.activeItem.deactivate();
37184                 delete this.activeItem;
37185             }
37186         }
37187         this.fireEvent("mouseout", this, e, t);
37188     },
37189
37190     /**
37191      * Read-only.  Returns true if the menu is currently displayed, else false.
37192      * @type Boolean
37193      */
37194     isVisible : function(){
37195         return this.el && !this.hidden;
37196     },
37197
37198     /**
37199      * Displays this menu relative to another element
37200      * @param {String/HTMLElement/Roo.Element} element The element to align to
37201      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37202      * the element (defaults to this.defaultAlign)
37203      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37204      */
37205     show : function(el, pos, parentMenu){
37206         this.parentMenu = parentMenu;
37207         if(!this.el){
37208             this.render();
37209         }
37210         this.fireEvent("beforeshow", this);
37211         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37212     },
37213
37214     /**
37215      * Displays this menu at a specific xy position
37216      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37217      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37218      */
37219     showAt : function(xy, parentMenu, /* private: */_e){
37220         this.parentMenu = parentMenu;
37221         if(!this.el){
37222             this.render();
37223         }
37224         if(_e !== false){
37225             this.fireEvent("beforeshow", this);
37226             xy = this.el.adjustForConstraints(xy);
37227         }
37228         this.el.setXY(xy);
37229         this.el.show();
37230         this.hidden = false;
37231         this.focus();
37232         this.fireEvent("show", this);
37233     },
37234
37235     focus : function(){
37236         if(!this.hidden){
37237             this.doFocus.defer(50, this);
37238         }
37239     },
37240
37241     doFocus : function(){
37242         if(!this.hidden){
37243             this.focusEl.focus();
37244         }
37245     },
37246
37247     /**
37248      * Hides this menu and optionally all parent menus
37249      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37250      */
37251     hide : function(deep){
37252         if(this.el && this.isVisible()){
37253             this.fireEvent("beforehide", this);
37254             if(this.activeItem){
37255                 this.activeItem.deactivate();
37256                 this.activeItem = null;
37257             }
37258             this.el.hide();
37259             this.hidden = true;
37260             this.fireEvent("hide", this);
37261         }
37262         if(deep === true && this.parentMenu){
37263             this.parentMenu.hide(true);
37264         }
37265     },
37266
37267     /**
37268      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37269      * Any of the following are valid:
37270      * <ul>
37271      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37272      * <li>An HTMLElement object which will be converted to a menu item</li>
37273      * <li>A menu item config object that will be created as a new menu item</li>
37274      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37275      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37276      * </ul>
37277      * Usage:
37278      * <pre><code>
37279 // Create the menu
37280 var menu = new Roo.menu.Menu();
37281
37282 // Create a menu item to add by reference
37283 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37284
37285 // Add a bunch of items at once using different methods.
37286 // Only the last item added will be returned.
37287 var item = menu.add(
37288     menuItem,                // add existing item by ref
37289     'Dynamic Item',          // new TextItem
37290     '-',                     // new separator
37291     { text: 'Config Item' }  // new item by config
37292 );
37293 </code></pre>
37294      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37295      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37296      */
37297     add : function(){
37298         var a = arguments, l = a.length, item;
37299         for(var i = 0; i < l; i++){
37300             var el = a[i];
37301             if ((typeof(el) == "object") && el.xtype && el.xns) {
37302                 el = Roo.factory(el, Roo.menu);
37303             }
37304             
37305             if(el.render){ // some kind of Item
37306                 item = this.addItem(el);
37307             }else if(typeof el == "string"){ // string
37308                 if(el == "separator" || el == "-"){
37309                     item = this.addSeparator();
37310                 }else{
37311                     item = this.addText(el);
37312                 }
37313             }else if(el.tagName || el.el){ // element
37314                 item = this.addElement(el);
37315             }else if(typeof el == "object"){ // must be menu item config?
37316                 item = this.addMenuItem(el);
37317             }
37318         }
37319         return item;
37320     },
37321
37322     /**
37323      * Returns this menu's underlying {@link Roo.Element} object
37324      * @return {Roo.Element} The element
37325      */
37326     getEl : function(){
37327         if(!this.el){
37328             this.render();
37329         }
37330         return this.el;
37331     },
37332
37333     /**
37334      * Adds a separator bar to the menu
37335      * @return {Roo.menu.Item} The menu item that was added
37336      */
37337     addSeparator : function(){
37338         return this.addItem(new Roo.menu.Separator());
37339     },
37340
37341     /**
37342      * Adds an {@link Roo.Element} object to the menu
37343      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37344      * @return {Roo.menu.Item} The menu item that was added
37345      */
37346     addElement : function(el){
37347         return this.addItem(new Roo.menu.BaseItem(el));
37348     },
37349
37350     /**
37351      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37352      * @param {Roo.menu.Item} item The menu item to add
37353      * @return {Roo.menu.Item} The menu item that was added
37354      */
37355     addItem : function(item){
37356         this.items.add(item);
37357         if(this.ul){
37358             var li = document.createElement("li");
37359             li.className = "x-menu-list-item";
37360             this.ul.dom.appendChild(li);
37361             item.render(li, this);
37362             this.delayAutoWidth();
37363         }
37364         return item;
37365     },
37366
37367     /**
37368      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37369      * @param {Object} config A MenuItem config object
37370      * @return {Roo.menu.Item} The menu item that was added
37371      */
37372     addMenuItem : function(config){
37373         if(!(config instanceof Roo.menu.Item)){
37374             if(typeof config.checked == "boolean"){ // must be check menu item config?
37375                 config = new Roo.menu.CheckItem(config);
37376             }else{
37377                 config = new Roo.menu.Item(config);
37378             }
37379         }
37380         return this.addItem(config);
37381     },
37382
37383     /**
37384      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37385      * @param {String} text The text to display in the menu item
37386      * @return {Roo.menu.Item} The menu item that was added
37387      */
37388     addText : function(text){
37389         return this.addItem(new Roo.menu.TextItem({ text : text }));
37390     },
37391
37392     /**
37393      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37394      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37395      * @param {Roo.menu.Item} item The menu item to add
37396      * @return {Roo.menu.Item} The menu item that was added
37397      */
37398     insert : function(index, item){
37399         this.items.insert(index, item);
37400         if(this.ul){
37401             var li = document.createElement("li");
37402             li.className = "x-menu-list-item";
37403             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37404             item.render(li, this);
37405             this.delayAutoWidth();
37406         }
37407         return item;
37408     },
37409
37410     /**
37411      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37412      * @param {Roo.menu.Item} item The menu item to remove
37413      */
37414     remove : function(item){
37415         this.items.removeKey(item.id);
37416         item.destroy();
37417     },
37418
37419     /**
37420      * Removes and destroys all items in the menu
37421      */
37422     removeAll : function(){
37423         var f;
37424         while(f = this.items.first()){
37425             this.remove(f);
37426         }
37427     }
37428 });
37429
37430 // MenuNav is a private utility class used internally by the Menu
37431 Roo.menu.MenuNav = function(menu){
37432     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37433     this.scope = this.menu = menu;
37434 };
37435
37436 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37437     doRelay : function(e, h){
37438         var k = e.getKey();
37439         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37440             this.menu.tryActivate(0, 1);
37441             return false;
37442         }
37443         return h.call(this.scope || this, e, this.menu);
37444     },
37445
37446     up : function(e, m){
37447         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37448             m.tryActivate(m.items.length-1, -1);
37449         }
37450     },
37451
37452     down : function(e, m){
37453         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37454             m.tryActivate(0, 1);
37455         }
37456     },
37457
37458     right : function(e, m){
37459         if(m.activeItem){
37460             m.activeItem.expandMenu(true);
37461         }
37462     },
37463
37464     left : function(e, m){
37465         m.hide();
37466         if(m.parentMenu && m.parentMenu.activeItem){
37467             m.parentMenu.activeItem.activate();
37468         }
37469     },
37470
37471     enter : function(e, m){
37472         if(m.activeItem){
37473             e.stopPropagation();
37474             m.activeItem.onClick(e);
37475             m.fireEvent("click", this, m.activeItem);
37476             return true;
37477         }
37478     }
37479 });/*
37480  * Based on:
37481  * Ext JS Library 1.1.1
37482  * Copyright(c) 2006-2007, Ext JS, LLC.
37483  *
37484  * Originally Released Under LGPL - original licence link has changed is not relivant.
37485  *
37486  * Fork - LGPL
37487  * <script type="text/javascript">
37488  */
37489  
37490 /**
37491  * @class Roo.menu.MenuMgr
37492  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37493  * @singleton
37494  */
37495 Roo.menu.MenuMgr = function(){
37496    var menus, active, groups = {}, attached = false, lastShow = new Date();
37497
37498    // private - called when first menu is created
37499    function init(){
37500        menus = {};
37501        active = new Roo.util.MixedCollection();
37502        Roo.get(document).addKeyListener(27, function(){
37503            if(active.length > 0){
37504                hideAll();
37505            }
37506        });
37507    }
37508
37509    // private
37510    function hideAll(){
37511        if(active && active.length > 0){
37512            var c = active.clone();
37513            c.each(function(m){
37514                m.hide();
37515            });
37516        }
37517    }
37518
37519    // private
37520    function onHide(m){
37521        active.remove(m);
37522        if(active.length < 1){
37523            Roo.get(document).un("mousedown", onMouseDown);
37524            attached = false;
37525        }
37526    }
37527
37528    // private
37529    function onShow(m){
37530        var last = active.last();
37531        lastShow = new Date();
37532        active.add(m);
37533        if(!attached){
37534            Roo.get(document).on("mousedown", onMouseDown);
37535            attached = true;
37536        }
37537        if(m.parentMenu){
37538           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37539           m.parentMenu.activeChild = m;
37540        }else if(last && last.isVisible()){
37541           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37542        }
37543    }
37544
37545    // private
37546    function onBeforeHide(m){
37547        if(m.activeChild){
37548            m.activeChild.hide();
37549        }
37550        if(m.autoHideTimer){
37551            clearTimeout(m.autoHideTimer);
37552            delete m.autoHideTimer;
37553        }
37554    }
37555
37556    // private
37557    function onBeforeShow(m){
37558        var pm = m.parentMenu;
37559        if(!pm && !m.allowOtherMenus){
37560            hideAll();
37561        }else if(pm && pm.activeChild && active != m){
37562            pm.activeChild.hide();
37563        }
37564    }
37565
37566    // private
37567    function onMouseDown(e){
37568        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37569            hideAll();
37570        }
37571    }
37572
37573    // private
37574    function onBeforeCheck(mi, state){
37575        if(state){
37576            var g = groups[mi.group];
37577            for(var i = 0, l = g.length; i < l; i++){
37578                if(g[i] != mi){
37579                    g[i].setChecked(false);
37580                }
37581            }
37582        }
37583    }
37584
37585    return {
37586
37587        /**
37588         * Hides all menus that are currently visible
37589         */
37590        hideAll : function(){
37591             hideAll();  
37592        },
37593
37594        // private
37595        register : function(menu){
37596            if(!menus){
37597                init();
37598            }
37599            menus[menu.id] = menu;
37600            menu.on("beforehide", onBeforeHide);
37601            menu.on("hide", onHide);
37602            menu.on("beforeshow", onBeforeShow);
37603            menu.on("show", onShow);
37604            var g = menu.group;
37605            if(g && menu.events["checkchange"]){
37606                if(!groups[g]){
37607                    groups[g] = [];
37608                }
37609                groups[g].push(menu);
37610                menu.on("checkchange", onCheck);
37611            }
37612        },
37613
37614         /**
37615          * Returns a {@link Roo.menu.Menu} object
37616          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37617          * be used to generate and return a new Menu instance.
37618          */
37619        get : function(menu){
37620            if(typeof menu == "string"){ // menu id
37621                return menus[menu];
37622            }else if(menu.events){  // menu instance
37623                return menu;
37624            }else if(typeof menu.length == 'number'){ // array of menu items?
37625                return new Roo.menu.Menu({items:menu});
37626            }else{ // otherwise, must be a config
37627                return new Roo.menu.Menu(menu);
37628            }
37629        },
37630
37631        // private
37632        unregister : function(menu){
37633            delete menus[menu.id];
37634            menu.un("beforehide", onBeforeHide);
37635            menu.un("hide", onHide);
37636            menu.un("beforeshow", onBeforeShow);
37637            menu.un("show", onShow);
37638            var g = menu.group;
37639            if(g && menu.events["checkchange"]){
37640                groups[g].remove(menu);
37641                menu.un("checkchange", onCheck);
37642            }
37643        },
37644
37645        // private
37646        registerCheckable : function(menuItem){
37647            var g = menuItem.group;
37648            if(g){
37649                if(!groups[g]){
37650                    groups[g] = [];
37651                }
37652                groups[g].push(menuItem);
37653                menuItem.on("beforecheckchange", onBeforeCheck);
37654            }
37655        },
37656
37657        // private
37658        unregisterCheckable : function(menuItem){
37659            var g = menuItem.group;
37660            if(g){
37661                groups[g].remove(menuItem);
37662                menuItem.un("beforecheckchange", onBeforeCheck);
37663            }
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.menu.BaseItem
37680  * @extends Roo.Component
37681  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37682  * management and base configuration options shared by all menu components.
37683  * @constructor
37684  * Creates a new BaseItem
37685  * @param {Object} config Configuration options
37686  */
37687 Roo.menu.BaseItem = function(config){
37688     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37689
37690     this.addEvents({
37691         /**
37692          * @event click
37693          * Fires when this item is clicked
37694          * @param {Roo.menu.BaseItem} this
37695          * @param {Roo.EventObject} e
37696          */
37697         click: true,
37698         /**
37699          * @event activate
37700          * Fires when this item is activated
37701          * @param {Roo.menu.BaseItem} this
37702          */
37703         activate : true,
37704         /**
37705          * @event deactivate
37706          * Fires when this item is deactivated
37707          * @param {Roo.menu.BaseItem} this
37708          */
37709         deactivate : true
37710     });
37711
37712     if(this.handler){
37713         this.on("click", this.handler, this.scope, true);
37714     }
37715 };
37716
37717 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37718     /**
37719      * @cfg {Function} handler
37720      * A function that will handle the click event of this menu item (defaults to undefined)
37721      */
37722     /**
37723      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37724      */
37725     canActivate : false,
37726     
37727      /**
37728      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37729      */
37730     hidden: false,
37731     
37732     /**
37733      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37734      */
37735     activeClass : "x-menu-item-active",
37736     /**
37737      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37738      */
37739     hideOnClick : true,
37740     /**
37741      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37742      */
37743     hideDelay : 100,
37744
37745     // private
37746     ctype: "Roo.menu.BaseItem",
37747
37748     // private
37749     actionMode : "container",
37750
37751     // private
37752     render : function(container, parentMenu){
37753         this.parentMenu = parentMenu;
37754         Roo.menu.BaseItem.superclass.render.call(this, container);
37755         this.container.menuItemId = this.id;
37756     },
37757
37758     // private
37759     onRender : function(container, position){
37760         this.el = Roo.get(this.el);
37761         container.dom.appendChild(this.el.dom);
37762     },
37763
37764     // private
37765     onClick : function(e){
37766         if(!this.disabled && this.fireEvent("click", this, e) !== false
37767                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37768             this.handleClick(e);
37769         }else{
37770             e.stopEvent();
37771         }
37772     },
37773
37774     // private
37775     activate : function(){
37776         if(this.disabled){
37777             return false;
37778         }
37779         var li = this.container;
37780         li.addClass(this.activeClass);
37781         this.region = li.getRegion().adjust(2, 2, -2, -2);
37782         this.fireEvent("activate", this);
37783         return true;
37784     },
37785
37786     // private
37787     deactivate : function(){
37788         this.container.removeClass(this.activeClass);
37789         this.fireEvent("deactivate", this);
37790     },
37791
37792     // private
37793     shouldDeactivate : function(e){
37794         return !this.region || !this.region.contains(e.getPoint());
37795     },
37796
37797     // private
37798     handleClick : function(e){
37799         if(this.hideOnClick){
37800             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37801         }
37802     },
37803
37804     // private
37805     expandMenu : function(autoActivate){
37806         // do nothing
37807     },
37808
37809     // private
37810     hideMenu : function(){
37811         // do nothing
37812     }
37813 });/*
37814  * Based on:
37815  * Ext JS Library 1.1.1
37816  * Copyright(c) 2006-2007, Ext JS, LLC.
37817  *
37818  * Originally Released Under LGPL - original licence link has changed is not relivant.
37819  *
37820  * Fork - LGPL
37821  * <script type="text/javascript">
37822  */
37823  
37824 /**
37825  * @class Roo.menu.Adapter
37826  * @extends Roo.menu.BaseItem
37827  * 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.
37828  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37829  * @constructor
37830  * Creates a new Adapter
37831  * @param {Object} config Configuration options
37832  */
37833 Roo.menu.Adapter = function(component, config){
37834     Roo.menu.Adapter.superclass.constructor.call(this, config);
37835     this.component = component;
37836 };
37837 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37838     // private
37839     canActivate : true,
37840
37841     // private
37842     onRender : function(container, position){
37843         this.component.render(container);
37844         this.el = this.component.getEl();
37845     },
37846
37847     // private
37848     activate : function(){
37849         if(this.disabled){
37850             return false;
37851         }
37852         this.component.focus();
37853         this.fireEvent("activate", this);
37854         return true;
37855     },
37856
37857     // private
37858     deactivate : function(){
37859         this.fireEvent("deactivate", this);
37860     },
37861
37862     // private
37863     disable : function(){
37864         this.component.disable();
37865         Roo.menu.Adapter.superclass.disable.call(this);
37866     },
37867
37868     // private
37869     enable : function(){
37870         this.component.enable();
37871         Roo.menu.Adapter.superclass.enable.call(this);
37872     }
37873 });/*
37874  * Based on:
37875  * Ext JS Library 1.1.1
37876  * Copyright(c) 2006-2007, Ext JS, LLC.
37877  *
37878  * Originally Released Under LGPL - original licence link has changed is not relivant.
37879  *
37880  * Fork - LGPL
37881  * <script type="text/javascript">
37882  */
37883
37884 /**
37885  * @class Roo.menu.TextItem
37886  * @extends Roo.menu.BaseItem
37887  * Adds a static text string to a menu, usually used as either a heading or group separator.
37888  * Note: old style constructor with text is still supported.
37889  * 
37890  * @constructor
37891  * Creates a new TextItem
37892  * @param {Object} cfg Configuration
37893  */
37894 Roo.menu.TextItem = function(cfg){
37895     if (typeof(cfg) == 'string') {
37896         this.text = cfg;
37897     } else {
37898         Roo.apply(this,cfg);
37899     }
37900     
37901     Roo.menu.TextItem.superclass.constructor.call(this);
37902 };
37903
37904 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
37905     /**
37906      * @cfg {Boolean} text Text to show on item.
37907      */
37908     text : '',
37909     
37910     /**
37911      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
37912      */
37913     hideOnClick : false,
37914     /**
37915      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
37916      */
37917     itemCls : "x-menu-text",
37918
37919     // private
37920     onRender : function(){
37921         var s = document.createElement("span");
37922         s.className = this.itemCls;
37923         s.innerHTML = this.text;
37924         this.el = s;
37925         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
37926     }
37927 });/*
37928  * Based on:
37929  * Ext JS Library 1.1.1
37930  * Copyright(c) 2006-2007, Ext JS, LLC.
37931  *
37932  * Originally Released Under LGPL - original licence link has changed is not relivant.
37933  *
37934  * Fork - LGPL
37935  * <script type="text/javascript">
37936  */
37937
37938 /**
37939  * @class Roo.menu.Separator
37940  * @extends Roo.menu.BaseItem
37941  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
37942  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
37943  * @constructor
37944  * @param {Object} config Configuration options
37945  */
37946 Roo.menu.Separator = function(config){
37947     Roo.menu.Separator.superclass.constructor.call(this, config);
37948 };
37949
37950 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
37951     /**
37952      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
37953      */
37954     itemCls : "x-menu-sep",
37955     /**
37956      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
37957      */
37958     hideOnClick : false,
37959
37960     // private
37961     onRender : function(li){
37962         var s = document.createElement("span");
37963         s.className = this.itemCls;
37964         s.innerHTML = "&#160;";
37965         this.el = s;
37966         li.addClass("x-menu-sep-li");
37967         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
37968     }
37969 });/*
37970  * Based on:
37971  * Ext JS Library 1.1.1
37972  * Copyright(c) 2006-2007, Ext JS, LLC.
37973  *
37974  * Originally Released Under LGPL - original licence link has changed is not relivant.
37975  *
37976  * Fork - LGPL
37977  * <script type="text/javascript">
37978  */
37979 /**
37980  * @class Roo.menu.Item
37981  * @extends Roo.menu.BaseItem
37982  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
37983  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
37984  * activation and click handling.
37985  * @constructor
37986  * Creates a new Item
37987  * @param {Object} config Configuration options
37988  */
37989 Roo.menu.Item = function(config){
37990     Roo.menu.Item.superclass.constructor.call(this, config);
37991     if(this.menu){
37992         this.menu = Roo.menu.MenuMgr.get(this.menu);
37993     }
37994 };
37995 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
37996     
37997     /**
37998      * @cfg {String} text
37999      * The text to show on the menu item.
38000      */
38001     text: '',
38002      /**
38003      * @cfg {String} HTML to render in menu
38004      * The text to show on the menu item (HTML version).
38005      */
38006     html: '',
38007     /**
38008      * @cfg {String} icon
38009      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38010      */
38011     icon: undefined,
38012     /**
38013      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38014      */
38015     itemCls : "x-menu-item",
38016     /**
38017      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38018      */
38019     canActivate : true,
38020     /**
38021      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38022      */
38023     showDelay: 200,
38024     // doc'd in BaseItem
38025     hideDelay: 200,
38026
38027     // private
38028     ctype: "Roo.menu.Item",
38029     
38030     // private
38031     onRender : function(container, position){
38032         var el = document.createElement("a");
38033         el.hideFocus = true;
38034         el.unselectable = "on";
38035         el.href = this.href || "#";
38036         if(this.hrefTarget){
38037             el.target = this.hrefTarget;
38038         }
38039         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38040         
38041         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38042         
38043         el.innerHTML = String.format(
38044                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38045                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38046         this.el = el;
38047         Roo.menu.Item.superclass.onRender.call(this, container, position);
38048     },
38049
38050     /**
38051      * Sets the text to display in this menu item
38052      * @param {String} text The text to display
38053      * @param {Boolean} isHTML true to indicate text is pure html.
38054      */
38055     setText : function(text, isHTML){
38056         if (isHTML) {
38057             this.html = text;
38058         } else {
38059             this.text = text;
38060             this.html = '';
38061         }
38062         if(this.rendered){
38063             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38064      
38065             this.el.update(String.format(
38066                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38067                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38068             this.parentMenu.autoWidth();
38069         }
38070     },
38071
38072     // private
38073     handleClick : function(e){
38074         if(!this.href){ // if no link defined, stop the event automatically
38075             e.stopEvent();
38076         }
38077         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38078     },
38079
38080     // private
38081     activate : function(autoExpand){
38082         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38083             this.focus();
38084             if(autoExpand){
38085                 this.expandMenu();
38086             }
38087         }
38088         return true;
38089     },
38090
38091     // private
38092     shouldDeactivate : function(e){
38093         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38094             if(this.menu && this.menu.isVisible()){
38095                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38096             }
38097             return true;
38098         }
38099         return false;
38100     },
38101
38102     // private
38103     deactivate : function(){
38104         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38105         this.hideMenu();
38106     },
38107
38108     // private
38109     expandMenu : function(autoActivate){
38110         if(!this.disabled && this.menu){
38111             clearTimeout(this.hideTimer);
38112             delete this.hideTimer;
38113             if(!this.menu.isVisible() && !this.showTimer){
38114                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38115             }else if (this.menu.isVisible() && autoActivate){
38116                 this.menu.tryActivate(0, 1);
38117             }
38118         }
38119     },
38120
38121     // private
38122     deferExpand : function(autoActivate){
38123         delete this.showTimer;
38124         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38125         if(autoActivate){
38126             this.menu.tryActivate(0, 1);
38127         }
38128     },
38129
38130     // private
38131     hideMenu : function(){
38132         clearTimeout(this.showTimer);
38133         delete this.showTimer;
38134         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38135             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38136         }
38137     },
38138
38139     // private
38140     deferHide : function(){
38141         delete this.hideTimer;
38142         this.menu.hide();
38143     }
38144 });/*
38145  * Based on:
38146  * Ext JS Library 1.1.1
38147  * Copyright(c) 2006-2007, Ext JS, LLC.
38148  *
38149  * Originally Released Under LGPL - original licence link has changed is not relivant.
38150  *
38151  * Fork - LGPL
38152  * <script type="text/javascript">
38153  */
38154  
38155 /**
38156  * @class Roo.menu.CheckItem
38157  * @extends Roo.menu.Item
38158  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38159  * @constructor
38160  * Creates a new CheckItem
38161  * @param {Object} config Configuration options
38162  */
38163 Roo.menu.CheckItem = function(config){
38164     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38165     this.addEvents({
38166         /**
38167          * @event beforecheckchange
38168          * Fires before the checked value is set, providing an opportunity to cancel if needed
38169          * @param {Roo.menu.CheckItem} this
38170          * @param {Boolean} checked The new checked value that will be set
38171          */
38172         "beforecheckchange" : true,
38173         /**
38174          * @event checkchange
38175          * Fires after the checked value has been set
38176          * @param {Roo.menu.CheckItem} this
38177          * @param {Boolean} checked The checked value that was set
38178          */
38179         "checkchange" : true
38180     });
38181     if(this.checkHandler){
38182         this.on('checkchange', this.checkHandler, this.scope);
38183     }
38184 };
38185 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38186     /**
38187      * @cfg {String} group
38188      * All check items with the same group name will automatically be grouped into a single-select
38189      * radio button group (defaults to '')
38190      */
38191     /**
38192      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38193      */
38194     itemCls : "x-menu-item x-menu-check-item",
38195     /**
38196      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38197      */
38198     groupClass : "x-menu-group-item",
38199
38200     /**
38201      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38202      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38203      * initialized with checked = true will be rendered as checked.
38204      */
38205     checked: false,
38206
38207     // private
38208     ctype: "Roo.menu.CheckItem",
38209
38210     // private
38211     onRender : function(c){
38212         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38213         if(this.group){
38214             this.el.addClass(this.groupClass);
38215         }
38216         Roo.menu.MenuMgr.registerCheckable(this);
38217         if(this.checked){
38218             this.checked = false;
38219             this.setChecked(true, true);
38220         }
38221     },
38222
38223     // private
38224     destroy : function(){
38225         if(this.rendered){
38226             Roo.menu.MenuMgr.unregisterCheckable(this);
38227         }
38228         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38229     },
38230
38231     /**
38232      * Set the checked state of this item
38233      * @param {Boolean} checked The new checked value
38234      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38235      */
38236     setChecked : function(state, suppressEvent){
38237         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38238             if(this.container){
38239                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38240             }
38241             this.checked = state;
38242             if(suppressEvent !== true){
38243                 this.fireEvent("checkchange", this, state);
38244             }
38245         }
38246     },
38247
38248     // private
38249     handleClick : function(e){
38250        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38251            this.setChecked(!this.checked);
38252        }
38253        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38254     }
38255 });/*
38256  * Based on:
38257  * Ext JS Library 1.1.1
38258  * Copyright(c) 2006-2007, Ext JS, LLC.
38259  *
38260  * Originally Released Under LGPL - original licence link has changed is not relivant.
38261  *
38262  * Fork - LGPL
38263  * <script type="text/javascript">
38264  */
38265  
38266 /**
38267  * @class Roo.menu.DateItem
38268  * @extends Roo.menu.Adapter
38269  * A menu item that wraps the {@link Roo.DatPicker} component.
38270  * @constructor
38271  * Creates a new DateItem
38272  * @param {Object} config Configuration options
38273  */
38274 Roo.menu.DateItem = function(config){
38275     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38276     /** The Roo.DatePicker object @type Roo.DatePicker */
38277     this.picker = this.component;
38278     this.addEvents({select: true});
38279     
38280     this.picker.on("render", function(picker){
38281         picker.getEl().swallowEvent("click");
38282         picker.container.addClass("x-menu-date-item");
38283     });
38284
38285     this.picker.on("select", this.onSelect, this);
38286 };
38287
38288 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38289     // private
38290     onSelect : function(picker, date){
38291         this.fireEvent("select", this, date, picker);
38292         Roo.menu.DateItem.superclass.handleClick.call(this);
38293     }
38294 });/*
38295  * Based on:
38296  * Ext JS Library 1.1.1
38297  * Copyright(c) 2006-2007, Ext JS, LLC.
38298  *
38299  * Originally Released Under LGPL - original licence link has changed is not relivant.
38300  *
38301  * Fork - LGPL
38302  * <script type="text/javascript">
38303  */
38304  
38305 /**
38306  * @class Roo.menu.ColorItem
38307  * @extends Roo.menu.Adapter
38308  * A menu item that wraps the {@link Roo.ColorPalette} component.
38309  * @constructor
38310  * Creates a new ColorItem
38311  * @param {Object} config Configuration options
38312  */
38313 Roo.menu.ColorItem = function(config){
38314     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38315     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38316     this.palette = this.component;
38317     this.relayEvents(this.palette, ["select"]);
38318     if(this.selectHandler){
38319         this.on('select', this.selectHandler, this.scope);
38320     }
38321 };
38322 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38323  * Based on:
38324  * Ext JS Library 1.1.1
38325  * Copyright(c) 2006-2007, Ext JS, LLC.
38326  *
38327  * Originally Released Under LGPL - original licence link has changed is not relivant.
38328  *
38329  * Fork - LGPL
38330  * <script type="text/javascript">
38331  */
38332  
38333
38334 /**
38335  * @class Roo.menu.DateMenu
38336  * @extends Roo.menu.Menu
38337  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38338  * @constructor
38339  * Creates a new DateMenu
38340  * @param {Object} config Configuration options
38341  */
38342 Roo.menu.DateMenu = function(config){
38343     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38344     this.plain = true;
38345     var di = new Roo.menu.DateItem(config);
38346     this.add(di);
38347     /**
38348      * The {@link Roo.DatePicker} instance for this DateMenu
38349      * @type DatePicker
38350      */
38351     this.picker = di.picker;
38352     /**
38353      * @event select
38354      * @param {DatePicker} picker
38355      * @param {Date} date
38356      */
38357     this.relayEvents(di, ["select"]);
38358     this.on('beforeshow', function(){
38359         if(this.picker){
38360             this.picker.hideMonthPicker(false);
38361         }
38362     }, this);
38363 };
38364 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38365     cls:'x-date-menu'
38366 });/*
38367  * Based on:
38368  * Ext JS Library 1.1.1
38369  * Copyright(c) 2006-2007, Ext JS, LLC.
38370  *
38371  * Originally Released Under LGPL - original licence link has changed is not relivant.
38372  *
38373  * Fork - LGPL
38374  * <script type="text/javascript">
38375  */
38376  
38377
38378 /**
38379  * @class Roo.menu.ColorMenu
38380  * @extends Roo.menu.Menu
38381  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38382  * @constructor
38383  * Creates a new ColorMenu
38384  * @param {Object} config Configuration options
38385  */
38386 Roo.menu.ColorMenu = function(config){
38387     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38388     this.plain = true;
38389     var ci = new Roo.menu.ColorItem(config);
38390     this.add(ci);
38391     /**
38392      * The {@link Roo.ColorPalette} instance for this ColorMenu
38393      * @type ColorPalette
38394      */
38395     this.palette = ci.palette;
38396     /**
38397      * @event select
38398      * @param {ColorPalette} palette
38399      * @param {String} color
38400      */
38401     this.relayEvents(ci, ["select"]);
38402 };
38403 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38404  * Based on:
38405  * Ext JS Library 1.1.1
38406  * Copyright(c) 2006-2007, Ext JS, LLC.
38407  *
38408  * Originally Released Under LGPL - original licence link has changed is not relivant.
38409  *
38410  * Fork - LGPL
38411  * <script type="text/javascript">
38412  */
38413  
38414 /**
38415  * @class Roo.form.Field
38416  * @extends Roo.BoxComponent
38417  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38418  * @constructor
38419  * Creates a new Field
38420  * @param {Object} config Configuration options
38421  */
38422 Roo.form.Field = function(config){
38423     Roo.form.Field.superclass.constructor.call(this, config);
38424 };
38425
38426 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38427     /**
38428      * @cfg {String} fieldLabel Label to use when rendering a form.
38429      */
38430        /**
38431      * @cfg {String} qtip Mouse over tip
38432      */
38433      
38434     /**
38435      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38436      */
38437     invalidClass : "x-form-invalid",
38438     /**
38439      * @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")
38440      */
38441     invalidText : "The value in this field is invalid",
38442     /**
38443      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38444      */
38445     focusClass : "x-form-focus",
38446     /**
38447      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38448       automatic validation (defaults to "keyup").
38449      */
38450     validationEvent : "keyup",
38451     /**
38452      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38453      */
38454     validateOnBlur : true,
38455     /**
38456      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38457      */
38458     validationDelay : 250,
38459     /**
38460      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38461      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38462      */
38463     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38464     /**
38465      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38466      */
38467     fieldClass : "x-form-field",
38468     /**
38469      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38470      *<pre>
38471 Value         Description
38472 -----------   ----------------------------------------------------------------------
38473 qtip          Display a quick tip when the user hovers over the field
38474 title         Display a default browser title attribute popup
38475 under         Add a block div beneath the field containing the error text
38476 side          Add an error icon to the right of the field with a popup on hover
38477 [element id]  Add the error text directly to the innerHTML of the specified element
38478 </pre>
38479      */
38480     msgTarget : 'qtip',
38481     /**
38482      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38483      */
38484     msgFx : 'normal',
38485
38486     /**
38487      * @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.
38488      */
38489     readOnly : false,
38490
38491     /**
38492      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38493      */
38494     disabled : false,
38495
38496     /**
38497      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38498      */
38499     inputType : undefined,
38500     
38501     /**
38502      * @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).
38503          */
38504         tabIndex : undefined,
38505         
38506     // private
38507     isFormField : true,
38508
38509     // private
38510     hasFocus : false,
38511     /**
38512      * @property {Roo.Element} fieldEl
38513      * Element Containing the rendered Field (with label etc.)
38514      */
38515     /**
38516      * @cfg {Mixed} value A value to initialize this field with.
38517      */
38518     value : undefined,
38519
38520     /**
38521      * @cfg {String} name The field's HTML name attribute.
38522      */
38523     /**
38524      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38525      */
38526     // private
38527     loadedValue : false,
38528      
38529      
38530         // private ??
38531         initComponent : function(){
38532         Roo.form.Field.superclass.initComponent.call(this);
38533         this.addEvents({
38534             /**
38535              * @event focus
38536              * Fires when this field receives input focus.
38537              * @param {Roo.form.Field} this
38538              */
38539             focus : true,
38540             /**
38541              * @event blur
38542              * Fires when this field loses input focus.
38543              * @param {Roo.form.Field} this
38544              */
38545             blur : true,
38546             /**
38547              * @event specialkey
38548              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38549              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38550              * @param {Roo.form.Field} this
38551              * @param {Roo.EventObject} e The event object
38552              */
38553             specialkey : true,
38554             /**
38555              * @event change
38556              * Fires just before the field blurs if the field value has changed.
38557              * @param {Roo.form.Field} this
38558              * @param {Mixed} newValue The new value
38559              * @param {Mixed} oldValue The original value
38560              */
38561             change : true,
38562             /**
38563              * @event invalid
38564              * Fires after the field has been marked as invalid.
38565              * @param {Roo.form.Field} this
38566              * @param {String} msg The validation message
38567              */
38568             invalid : true,
38569             /**
38570              * @event valid
38571              * Fires after the field has been validated with no errors.
38572              * @param {Roo.form.Field} this
38573              */
38574             valid : true,
38575              /**
38576              * @event keyup
38577              * Fires after the key up
38578              * @param {Roo.form.Field} this
38579              * @param {Roo.EventObject}  e The event Object
38580              */
38581             keyup : true
38582         });
38583     },
38584
38585     /**
38586      * Returns the name attribute of the field if available
38587      * @return {String} name The field name
38588      */
38589     getName: function(){
38590          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38591     },
38592
38593     // private
38594     onRender : function(ct, position){
38595         Roo.form.Field.superclass.onRender.call(this, ct, position);
38596         if(!this.el){
38597             var cfg = this.getAutoCreate();
38598             if(!cfg.name){
38599                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38600             }
38601             if (!cfg.name.length) {
38602                 delete cfg.name;
38603             }
38604             if(this.inputType){
38605                 cfg.type = this.inputType;
38606             }
38607             this.el = ct.createChild(cfg, position);
38608         }
38609         var type = this.el.dom.type;
38610         if(type){
38611             if(type == 'password'){
38612                 type = 'text';
38613             }
38614             this.el.addClass('x-form-'+type);
38615         }
38616         if(this.readOnly){
38617             this.el.dom.readOnly = true;
38618         }
38619         if(this.tabIndex !== undefined){
38620             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38621         }
38622
38623         this.el.addClass([this.fieldClass, this.cls]);
38624         this.initValue();
38625     },
38626
38627     /**
38628      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38629      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38630      * @return {Roo.form.Field} this
38631      */
38632     applyTo : function(target){
38633         this.allowDomMove = false;
38634         this.el = Roo.get(target);
38635         this.render(this.el.dom.parentNode);
38636         return this;
38637     },
38638
38639     // private
38640     initValue : function(){
38641         if(this.value !== undefined){
38642             this.setValue(this.value);
38643         }else if(this.el.dom.value.length > 0){
38644             this.setValue(this.el.dom.value);
38645         }
38646     },
38647
38648     /**
38649      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38650      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38651      */
38652     isDirty : function() {
38653         if(this.disabled) {
38654             return false;
38655         }
38656         return String(this.getValue()) !== String(this.originalValue);
38657     },
38658
38659     /**
38660      * stores the current value in loadedValue
38661      */
38662     resetHasChanged : function()
38663     {
38664         this.loadedValue = String(this.getValue());
38665     },
38666     /**
38667      * checks the current value against the 'loaded' value.
38668      * Note - will return false if 'resetHasChanged' has not been called first.
38669      */
38670     hasChanged : function()
38671     {
38672         if(this.disabled || this.readOnly) {
38673             return false;
38674         }
38675         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38676     },
38677     
38678     
38679     
38680     // private
38681     afterRender : function(){
38682         Roo.form.Field.superclass.afterRender.call(this);
38683         this.initEvents();
38684     },
38685
38686     // private
38687     fireKey : function(e){
38688         //Roo.log('field ' + e.getKey());
38689         if(e.isNavKeyPress()){
38690             this.fireEvent("specialkey", this, e);
38691         }
38692     },
38693
38694     /**
38695      * Resets the current field value to the originally loaded value and clears any validation messages
38696      */
38697     reset : function(){
38698         this.setValue(this.resetValue);
38699         this.clearInvalid();
38700     },
38701
38702     // private
38703     initEvents : function(){
38704         // safari killled keypress - so keydown is now used..
38705         this.el.on("keydown" , this.fireKey,  this);
38706         this.el.on("focus", this.onFocus,  this);
38707         this.el.on("blur", this.onBlur,  this);
38708         this.el.relayEvent('keyup', this);
38709
38710         // reference to original value for reset
38711         this.originalValue = this.getValue();
38712         this.resetValue =  this.getValue();
38713     },
38714
38715     // private
38716     onFocus : function(){
38717         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38718             this.el.addClass(this.focusClass);
38719         }
38720         if(!this.hasFocus){
38721             this.hasFocus = true;
38722             this.startValue = this.getValue();
38723             this.fireEvent("focus", this);
38724         }
38725     },
38726
38727     beforeBlur : Roo.emptyFn,
38728
38729     // private
38730     onBlur : function(){
38731         this.beforeBlur();
38732         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38733             this.el.removeClass(this.focusClass);
38734         }
38735         this.hasFocus = false;
38736         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38737             this.validate();
38738         }
38739         var v = this.getValue();
38740         if(String(v) !== String(this.startValue)){
38741             this.fireEvent('change', this, v, this.startValue);
38742         }
38743         this.fireEvent("blur", this);
38744     },
38745
38746     /**
38747      * Returns whether or not the field value is currently valid
38748      * @param {Boolean} preventMark True to disable marking the field invalid
38749      * @return {Boolean} True if the value is valid, else false
38750      */
38751     isValid : function(preventMark){
38752         if(this.disabled){
38753             return true;
38754         }
38755         var restore = this.preventMark;
38756         this.preventMark = preventMark === true;
38757         var v = this.validateValue(this.processValue(this.getRawValue()));
38758         this.preventMark = restore;
38759         return v;
38760     },
38761
38762     /**
38763      * Validates the field value
38764      * @return {Boolean} True if the value is valid, else false
38765      */
38766     validate : function(){
38767         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38768             this.clearInvalid();
38769             return true;
38770         }
38771         return false;
38772     },
38773
38774     processValue : function(value){
38775         return value;
38776     },
38777
38778     // private
38779     // Subclasses should provide the validation implementation by overriding this
38780     validateValue : function(value){
38781         return true;
38782     },
38783
38784     /**
38785      * Mark this field as invalid
38786      * @param {String} msg The validation message
38787      */
38788     markInvalid : function(msg){
38789         if(!this.rendered || this.preventMark){ // not rendered
38790             return;
38791         }
38792         
38793         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38794         
38795         obj.el.addClass(this.invalidClass);
38796         msg = msg || this.invalidText;
38797         switch(this.msgTarget){
38798             case 'qtip':
38799                 obj.el.dom.qtip = msg;
38800                 obj.el.dom.qclass = 'x-form-invalid-tip';
38801                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38802                     Roo.QuickTips.enable();
38803                 }
38804                 break;
38805             case 'title':
38806                 this.el.dom.title = msg;
38807                 break;
38808             case 'under':
38809                 if(!this.errorEl){
38810                     var elp = this.el.findParent('.x-form-element', 5, true);
38811                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38812                     this.errorEl.setWidth(elp.getWidth(true)-20);
38813                 }
38814                 this.errorEl.update(msg);
38815                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38816                 break;
38817             case 'side':
38818                 if(!this.errorIcon){
38819                     var elp = this.el.findParent('.x-form-element', 5, true);
38820                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38821                 }
38822                 this.alignErrorIcon();
38823                 this.errorIcon.dom.qtip = msg;
38824                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38825                 this.errorIcon.show();
38826                 this.on('resize', this.alignErrorIcon, this);
38827                 break;
38828             default:
38829                 var t = Roo.getDom(this.msgTarget);
38830                 t.innerHTML = msg;
38831                 t.style.display = this.msgDisplay;
38832                 break;
38833         }
38834         this.fireEvent('invalid', this, msg);
38835     },
38836
38837     // private
38838     alignErrorIcon : function(){
38839         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38840     },
38841
38842     /**
38843      * Clear any invalid styles/messages for this field
38844      */
38845     clearInvalid : function(){
38846         if(!this.rendered || this.preventMark){ // not rendered
38847             return;
38848         }
38849         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38850         
38851         obj.el.removeClass(this.invalidClass);
38852         switch(this.msgTarget){
38853             case 'qtip':
38854                 obj.el.dom.qtip = '';
38855                 break;
38856             case 'title':
38857                 this.el.dom.title = '';
38858                 break;
38859             case 'under':
38860                 if(this.errorEl){
38861                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
38862                 }
38863                 break;
38864             case 'side':
38865                 if(this.errorIcon){
38866                     this.errorIcon.dom.qtip = '';
38867                     this.errorIcon.hide();
38868                     this.un('resize', this.alignErrorIcon, this);
38869                 }
38870                 break;
38871             default:
38872                 var t = Roo.getDom(this.msgTarget);
38873                 t.innerHTML = '';
38874                 t.style.display = 'none';
38875                 break;
38876         }
38877         this.fireEvent('valid', this);
38878     },
38879
38880     /**
38881      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
38882      * @return {Mixed} value The field value
38883      */
38884     getRawValue : function(){
38885         var v = this.el.getValue();
38886         
38887         return v;
38888     },
38889
38890     /**
38891      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
38892      * @return {Mixed} value The field value
38893      */
38894     getValue : function(){
38895         var v = this.el.getValue();
38896          
38897         return v;
38898     },
38899
38900     /**
38901      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
38902      * @param {Mixed} value The value to set
38903      */
38904     setRawValue : function(v){
38905         return this.el.dom.value = (v === null || v === undefined ? '' : v);
38906     },
38907
38908     /**
38909      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
38910      * @param {Mixed} value The value to set
38911      */
38912     setValue : function(v){
38913         this.value = v;
38914         if(this.rendered){
38915             this.el.dom.value = (v === null || v === undefined ? '' : v);
38916              this.validate();
38917         }
38918     },
38919
38920     adjustSize : function(w, h){
38921         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
38922         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
38923         return s;
38924     },
38925
38926     adjustWidth : function(tag, w){
38927         tag = tag.toLowerCase();
38928         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
38929             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
38930                 if(tag == 'input'){
38931                     return w + 2;
38932                 }
38933                 if(tag == 'textarea'){
38934                     return w-2;
38935                 }
38936             }else if(Roo.isOpera){
38937                 if(tag == 'input'){
38938                     return w + 2;
38939                 }
38940                 if(tag == 'textarea'){
38941                     return w-2;
38942                 }
38943             }
38944         }
38945         return w;
38946     }
38947 });
38948
38949
38950 // anything other than normal should be considered experimental
38951 Roo.form.Field.msgFx = {
38952     normal : {
38953         show: function(msgEl, f){
38954             msgEl.setDisplayed('block');
38955         },
38956
38957         hide : function(msgEl, f){
38958             msgEl.setDisplayed(false).update('');
38959         }
38960     },
38961
38962     slide : {
38963         show: function(msgEl, f){
38964             msgEl.slideIn('t', {stopFx:true});
38965         },
38966
38967         hide : function(msgEl, f){
38968             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
38969         }
38970     },
38971
38972     slideRight : {
38973         show: function(msgEl, f){
38974             msgEl.fixDisplay();
38975             msgEl.alignTo(f.el, 'tl-tr');
38976             msgEl.slideIn('l', {stopFx:true});
38977         },
38978
38979         hide : function(msgEl, f){
38980             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
38981         }
38982     }
38983 };/*
38984  * Based on:
38985  * Ext JS Library 1.1.1
38986  * Copyright(c) 2006-2007, Ext JS, LLC.
38987  *
38988  * Originally Released Under LGPL - original licence link has changed is not relivant.
38989  *
38990  * Fork - LGPL
38991  * <script type="text/javascript">
38992  */
38993  
38994
38995 /**
38996  * @class Roo.form.TextField
38997  * @extends Roo.form.Field
38998  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
38999  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39000  * @constructor
39001  * Creates a new TextField
39002  * @param {Object} config Configuration options
39003  */
39004 Roo.form.TextField = function(config){
39005     Roo.form.TextField.superclass.constructor.call(this, config);
39006     this.addEvents({
39007         /**
39008          * @event autosize
39009          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39010          * according to the default logic, but this event provides a hook for the developer to apply additional
39011          * logic at runtime to resize the field if needed.
39012              * @param {Roo.form.Field} this This text field
39013              * @param {Number} width The new field width
39014              */
39015         autosize : true
39016     });
39017 };
39018
39019 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39020     /**
39021      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39022      */
39023     grow : false,
39024     /**
39025      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39026      */
39027     growMin : 30,
39028     /**
39029      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39030      */
39031     growMax : 800,
39032     /**
39033      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39034      */
39035     vtype : null,
39036     /**
39037      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39038      */
39039     maskRe : null,
39040     /**
39041      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39042      */
39043     disableKeyFilter : false,
39044     /**
39045      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39046      */
39047     allowBlank : true,
39048     /**
39049      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39050      */
39051     minLength : 0,
39052     /**
39053      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39054      */
39055     maxLength : Number.MAX_VALUE,
39056     /**
39057      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39058      */
39059     minLengthText : "The minimum length for this field is {0}",
39060     /**
39061      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39062      */
39063     maxLengthText : "The maximum length for this field is {0}",
39064     /**
39065      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39066      */
39067     selectOnFocus : false,
39068     /**
39069      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39070      */
39071     blankText : "This field is required",
39072     /**
39073      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39074      * If available, this function will be called only after the basic validators all return true, and will be passed the
39075      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39076      */
39077     validator : null,
39078     /**
39079      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39080      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39081      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39082      */
39083     regex : null,
39084     /**
39085      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39086      */
39087     regexText : "",
39088     /**
39089      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39090      */
39091     emptyText : null,
39092    
39093
39094     // private
39095     initEvents : function()
39096     {
39097         if (this.emptyText) {
39098             this.el.attr('placeholder', this.emptyText);
39099         }
39100         
39101         Roo.form.TextField.superclass.initEvents.call(this);
39102         if(this.validationEvent == 'keyup'){
39103             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39104             this.el.on('keyup', this.filterValidation, this);
39105         }
39106         else if(this.validationEvent !== false){
39107             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39108         }
39109         
39110         if(this.selectOnFocus){
39111             this.on("focus", this.preFocus, this);
39112             
39113         }
39114         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39115             this.el.on("keypress", this.filterKeys, this);
39116         }
39117         if(this.grow){
39118             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39119             this.el.on("click", this.autoSize,  this);
39120         }
39121         if(this.el.is('input[type=password]') && Roo.isSafari){
39122             this.el.on('keydown', this.SafariOnKeyDown, this);
39123         }
39124     },
39125
39126     processValue : function(value){
39127         if(this.stripCharsRe){
39128             var newValue = value.replace(this.stripCharsRe, '');
39129             if(newValue !== value){
39130                 this.setRawValue(newValue);
39131                 return newValue;
39132             }
39133         }
39134         return value;
39135     },
39136
39137     filterValidation : function(e){
39138         if(!e.isNavKeyPress()){
39139             this.validationTask.delay(this.validationDelay);
39140         }
39141     },
39142
39143     // private
39144     onKeyUp : function(e){
39145         if(!e.isNavKeyPress()){
39146             this.autoSize();
39147         }
39148     },
39149
39150     /**
39151      * Resets the current field value to the originally-loaded value and clears any validation messages.
39152      *  
39153      */
39154     reset : function(){
39155         Roo.form.TextField.superclass.reset.call(this);
39156        
39157     },
39158
39159     
39160     // private
39161     preFocus : function(){
39162         
39163         if(this.selectOnFocus){
39164             this.el.dom.select();
39165         }
39166     },
39167
39168     
39169     // private
39170     filterKeys : function(e){
39171         var k = e.getKey();
39172         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39173             return;
39174         }
39175         var c = e.getCharCode(), cc = String.fromCharCode(c);
39176         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39177             return;
39178         }
39179         if(!this.maskRe.test(cc)){
39180             e.stopEvent();
39181         }
39182     },
39183
39184     setValue : function(v){
39185         
39186         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39187         
39188         this.autoSize();
39189     },
39190
39191     /**
39192      * Validates a value according to the field's validation rules and marks the field as invalid
39193      * if the validation fails
39194      * @param {Mixed} value The value to validate
39195      * @return {Boolean} True if the value is valid, else false
39196      */
39197     validateValue : function(value){
39198         if(value.length < 1)  { // if it's blank
39199              if(this.allowBlank){
39200                 this.clearInvalid();
39201                 return true;
39202              }else{
39203                 this.markInvalid(this.blankText);
39204                 return false;
39205              }
39206         }
39207         if(value.length < this.minLength){
39208             this.markInvalid(String.format(this.minLengthText, this.minLength));
39209             return false;
39210         }
39211         if(value.length > this.maxLength){
39212             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39213             return false;
39214         }
39215         if(this.vtype){
39216             var vt = Roo.form.VTypes;
39217             if(!vt[this.vtype](value, this)){
39218                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39219                 return false;
39220             }
39221         }
39222         if(typeof this.validator == "function"){
39223             var msg = this.validator(value);
39224             if(msg !== true){
39225                 this.markInvalid(msg);
39226                 return false;
39227             }
39228         }
39229         if(this.regex && !this.regex.test(value)){
39230             this.markInvalid(this.regexText);
39231             return false;
39232         }
39233         return true;
39234     },
39235
39236     /**
39237      * Selects text in this field
39238      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39239      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39240      */
39241     selectText : function(start, end){
39242         var v = this.getRawValue();
39243         if(v.length > 0){
39244             start = start === undefined ? 0 : start;
39245             end = end === undefined ? v.length : end;
39246             var d = this.el.dom;
39247             if(d.setSelectionRange){
39248                 d.setSelectionRange(start, end);
39249             }else if(d.createTextRange){
39250                 var range = d.createTextRange();
39251                 range.moveStart("character", start);
39252                 range.moveEnd("character", v.length-end);
39253                 range.select();
39254             }
39255         }
39256     },
39257
39258     /**
39259      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39260      * This only takes effect if grow = true, and fires the autosize event.
39261      */
39262     autoSize : function(){
39263         if(!this.grow || !this.rendered){
39264             return;
39265         }
39266         if(!this.metrics){
39267             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39268         }
39269         var el = this.el;
39270         var v = el.dom.value;
39271         var d = document.createElement('div');
39272         d.appendChild(document.createTextNode(v));
39273         v = d.innerHTML;
39274         d = null;
39275         v += "&#160;";
39276         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39277         this.el.setWidth(w);
39278         this.fireEvent("autosize", this, w);
39279     },
39280     
39281     // private
39282     SafariOnKeyDown : function(event)
39283     {
39284         // this is a workaround for a password hang bug on chrome/ webkit.
39285         
39286         var isSelectAll = false;
39287         
39288         if(this.el.dom.selectionEnd > 0){
39289             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39290         }
39291         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39292             event.preventDefault();
39293             this.setValue('');
39294             return;
39295         }
39296         
39297         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39298             
39299             event.preventDefault();
39300             // this is very hacky as keydown always get's upper case.
39301             
39302             var cc = String.fromCharCode(event.getCharCode());
39303             
39304             
39305             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39306             
39307         }
39308         
39309         
39310     }
39311 });/*
39312  * Based on:
39313  * Ext JS Library 1.1.1
39314  * Copyright(c) 2006-2007, Ext JS, LLC.
39315  *
39316  * Originally Released Under LGPL - original licence link has changed is not relivant.
39317  *
39318  * Fork - LGPL
39319  * <script type="text/javascript">
39320  */
39321  
39322 /**
39323  * @class Roo.form.Hidden
39324  * @extends Roo.form.TextField
39325  * Simple Hidden element used on forms 
39326  * 
39327  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39328  * 
39329  * @constructor
39330  * Creates a new Hidden form element.
39331  * @param {Object} config Configuration options
39332  */
39333
39334
39335
39336 // easy hidden field...
39337 Roo.form.Hidden = function(config){
39338     Roo.form.Hidden.superclass.constructor.call(this, config);
39339 };
39340   
39341 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39342     fieldLabel:      '',
39343     inputType:      'hidden',
39344     width:          50,
39345     allowBlank:     true,
39346     labelSeparator: '',
39347     hidden:         true,
39348     itemCls :       'x-form-item-display-none'
39349
39350
39351 });
39352
39353
39354 /*
39355  * Based on:
39356  * Ext JS Library 1.1.1
39357  * Copyright(c) 2006-2007, Ext JS, LLC.
39358  *
39359  * Originally Released Under LGPL - original licence link has changed is not relivant.
39360  *
39361  * Fork - LGPL
39362  * <script type="text/javascript">
39363  */
39364  
39365 /**
39366  * @class Roo.form.TriggerField
39367  * @extends Roo.form.TextField
39368  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39369  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39370  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39371  * for which you can provide a custom implementation.  For example:
39372  * <pre><code>
39373 var trigger = new Roo.form.TriggerField();
39374 trigger.onTriggerClick = myTriggerFn;
39375 trigger.applyTo('my-field');
39376 </code></pre>
39377  *
39378  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39379  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39380  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39381  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39382  * @constructor
39383  * Create a new TriggerField.
39384  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39385  * to the base TextField)
39386  */
39387 Roo.form.TriggerField = function(config){
39388     this.mimicing = false;
39389     Roo.form.TriggerField.superclass.constructor.call(this, config);
39390 };
39391
39392 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39393     /**
39394      * @cfg {String} triggerClass A CSS class to apply to the trigger
39395      */
39396     /**
39397      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39398      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39399      */
39400     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39401     /**
39402      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39403      */
39404     hideTrigger:false,
39405
39406     /** @cfg {Boolean} grow @hide */
39407     /** @cfg {Number} growMin @hide */
39408     /** @cfg {Number} growMax @hide */
39409
39410     /**
39411      * @hide 
39412      * @method
39413      */
39414     autoSize: Roo.emptyFn,
39415     // private
39416     monitorTab : true,
39417     // private
39418     deferHeight : true,
39419
39420     
39421     actionMode : 'wrap',
39422     // private
39423     onResize : function(w, h){
39424         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39425         if(typeof w == 'number'){
39426             var x = w - this.trigger.getWidth();
39427             this.el.setWidth(this.adjustWidth('input', x));
39428             this.trigger.setStyle('left', x+'px');
39429         }
39430     },
39431
39432     // private
39433     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39434
39435     // private
39436     getResizeEl : function(){
39437         return this.wrap;
39438     },
39439
39440     // private
39441     getPositionEl : function(){
39442         return this.wrap;
39443     },
39444
39445     // private
39446     alignErrorIcon : function(){
39447         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39448     },
39449
39450     // private
39451     onRender : function(ct, position){
39452         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39453         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39454         this.trigger = this.wrap.createChild(this.triggerConfig ||
39455                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39456         if(this.hideTrigger){
39457             this.trigger.setDisplayed(false);
39458         }
39459         this.initTrigger();
39460         if(!this.width){
39461             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39462         }
39463     },
39464
39465     // private
39466     initTrigger : function(){
39467         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39468         this.trigger.addClassOnOver('x-form-trigger-over');
39469         this.trigger.addClassOnClick('x-form-trigger-click');
39470     },
39471
39472     // private
39473     onDestroy : function(){
39474         if(this.trigger){
39475             this.trigger.removeAllListeners();
39476             this.trigger.remove();
39477         }
39478         if(this.wrap){
39479             this.wrap.remove();
39480         }
39481         Roo.form.TriggerField.superclass.onDestroy.call(this);
39482     },
39483
39484     // private
39485     onFocus : function(){
39486         Roo.form.TriggerField.superclass.onFocus.call(this);
39487         if(!this.mimicing){
39488             this.wrap.addClass('x-trigger-wrap-focus');
39489             this.mimicing = true;
39490             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39491             if(this.monitorTab){
39492                 this.el.on("keydown", this.checkTab, this);
39493             }
39494         }
39495     },
39496
39497     // private
39498     checkTab : function(e){
39499         if(e.getKey() == e.TAB){
39500             this.triggerBlur();
39501         }
39502     },
39503
39504     // private
39505     onBlur : function(){
39506         // do nothing
39507     },
39508
39509     // private
39510     mimicBlur : function(e, t){
39511         if(!this.wrap.contains(t) && this.validateBlur()){
39512             this.triggerBlur();
39513         }
39514     },
39515
39516     // private
39517     triggerBlur : function(){
39518         this.mimicing = false;
39519         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39520         if(this.monitorTab){
39521             this.el.un("keydown", this.checkTab, this);
39522         }
39523         this.wrap.removeClass('x-trigger-wrap-focus');
39524         Roo.form.TriggerField.superclass.onBlur.call(this);
39525     },
39526
39527     // private
39528     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39529     validateBlur : function(e, t){
39530         return true;
39531     },
39532
39533     // private
39534     onDisable : function(){
39535         Roo.form.TriggerField.superclass.onDisable.call(this);
39536         if(this.wrap){
39537             this.wrap.addClass('x-item-disabled');
39538         }
39539     },
39540
39541     // private
39542     onEnable : function(){
39543         Roo.form.TriggerField.superclass.onEnable.call(this);
39544         if(this.wrap){
39545             this.wrap.removeClass('x-item-disabled');
39546         }
39547     },
39548
39549     // private
39550     onShow : function(){
39551         var ae = this.getActionEl();
39552         
39553         if(ae){
39554             ae.dom.style.display = '';
39555             ae.dom.style.visibility = 'visible';
39556         }
39557     },
39558
39559     // private
39560     
39561     onHide : function(){
39562         var ae = this.getActionEl();
39563         ae.dom.style.display = 'none';
39564     },
39565
39566     /**
39567      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39568      * by an implementing function.
39569      * @method
39570      * @param {EventObject} e
39571      */
39572     onTriggerClick : Roo.emptyFn
39573 });
39574
39575 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39576 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39577 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39578 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39579     initComponent : function(){
39580         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39581
39582         this.triggerConfig = {
39583             tag:'span', cls:'x-form-twin-triggers', cn:[
39584             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39585             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39586         ]};
39587     },
39588
39589     getTrigger : function(index){
39590         return this.triggers[index];
39591     },
39592
39593     initTrigger : function(){
39594         var ts = this.trigger.select('.x-form-trigger', true);
39595         this.wrap.setStyle('overflow', 'hidden');
39596         var triggerField = this;
39597         ts.each(function(t, all, index){
39598             t.hide = function(){
39599                 var w = triggerField.wrap.getWidth();
39600                 this.dom.style.display = 'none';
39601                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39602             };
39603             t.show = function(){
39604                 var w = triggerField.wrap.getWidth();
39605                 this.dom.style.display = '';
39606                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39607             };
39608             var triggerIndex = 'Trigger'+(index+1);
39609
39610             if(this['hide'+triggerIndex]){
39611                 t.dom.style.display = 'none';
39612             }
39613             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39614             t.addClassOnOver('x-form-trigger-over');
39615             t.addClassOnClick('x-form-trigger-click');
39616         }, this);
39617         this.triggers = ts.elements;
39618     },
39619
39620     onTrigger1Click : Roo.emptyFn,
39621     onTrigger2Click : Roo.emptyFn
39622 });/*
39623  * Based on:
39624  * Ext JS Library 1.1.1
39625  * Copyright(c) 2006-2007, Ext JS, LLC.
39626  *
39627  * Originally Released Under LGPL - original licence link has changed is not relivant.
39628  *
39629  * Fork - LGPL
39630  * <script type="text/javascript">
39631  */
39632  
39633 /**
39634  * @class Roo.form.TextArea
39635  * @extends Roo.form.TextField
39636  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39637  * support for auto-sizing.
39638  * @constructor
39639  * Creates a new TextArea
39640  * @param {Object} config Configuration options
39641  */
39642 Roo.form.TextArea = function(config){
39643     Roo.form.TextArea.superclass.constructor.call(this, config);
39644     // these are provided exchanges for backwards compat
39645     // minHeight/maxHeight were replaced by growMin/growMax to be
39646     // compatible with TextField growing config values
39647     if(this.minHeight !== undefined){
39648         this.growMin = this.minHeight;
39649     }
39650     if(this.maxHeight !== undefined){
39651         this.growMax = this.maxHeight;
39652     }
39653 };
39654
39655 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39656     /**
39657      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39658      */
39659     growMin : 60,
39660     /**
39661      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39662      */
39663     growMax: 1000,
39664     /**
39665      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39666      * in the field (equivalent to setting overflow: hidden, defaults to false)
39667      */
39668     preventScrollbars: false,
39669     /**
39670      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39671      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39672      */
39673
39674     // private
39675     onRender : function(ct, position){
39676         if(!this.el){
39677             this.defaultAutoCreate = {
39678                 tag: "textarea",
39679                 style:"width:300px;height:60px;",
39680                 autocomplete: "new-password"
39681             };
39682         }
39683         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39684         if(this.grow){
39685             this.textSizeEl = Roo.DomHelper.append(document.body, {
39686                 tag: "pre", cls: "x-form-grow-sizer"
39687             });
39688             if(this.preventScrollbars){
39689                 this.el.setStyle("overflow", "hidden");
39690             }
39691             this.el.setHeight(this.growMin);
39692         }
39693     },
39694
39695     onDestroy : function(){
39696         if(this.textSizeEl){
39697             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39698         }
39699         Roo.form.TextArea.superclass.onDestroy.call(this);
39700     },
39701
39702     // private
39703     onKeyUp : function(e){
39704         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39705             this.autoSize();
39706         }
39707     },
39708
39709     /**
39710      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39711      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39712      */
39713     autoSize : function(){
39714         if(!this.grow || !this.textSizeEl){
39715             return;
39716         }
39717         var el = this.el;
39718         var v = el.dom.value;
39719         var ts = this.textSizeEl;
39720
39721         ts.innerHTML = '';
39722         ts.appendChild(document.createTextNode(v));
39723         v = ts.innerHTML;
39724
39725         Roo.fly(ts).setWidth(this.el.getWidth());
39726         if(v.length < 1){
39727             v = "&#160;&#160;";
39728         }else{
39729             if(Roo.isIE){
39730                 v = v.replace(/\n/g, '<p>&#160;</p>');
39731             }
39732             v += "&#160;\n&#160;";
39733         }
39734         ts.innerHTML = v;
39735         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39736         if(h != this.lastHeight){
39737             this.lastHeight = h;
39738             this.el.setHeight(h);
39739             this.fireEvent("autosize", this, h);
39740         }
39741     }
39742 });/*
39743  * Based on:
39744  * Ext JS Library 1.1.1
39745  * Copyright(c) 2006-2007, Ext JS, LLC.
39746  *
39747  * Originally Released Under LGPL - original licence link has changed is not relivant.
39748  *
39749  * Fork - LGPL
39750  * <script type="text/javascript">
39751  */
39752  
39753
39754 /**
39755  * @class Roo.form.NumberField
39756  * @extends Roo.form.TextField
39757  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39758  * @constructor
39759  * Creates a new NumberField
39760  * @param {Object} config Configuration options
39761  */
39762 Roo.form.NumberField = function(config){
39763     Roo.form.NumberField.superclass.constructor.call(this, config);
39764 };
39765
39766 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39767     /**
39768      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39769      */
39770     fieldClass: "x-form-field x-form-num-field",
39771     /**
39772      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39773      */
39774     allowDecimals : true,
39775     /**
39776      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39777      */
39778     decimalSeparator : ".",
39779     /**
39780      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39781      */
39782     decimalPrecision : 2,
39783     /**
39784      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39785      */
39786     allowNegative : true,
39787     /**
39788      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39789      */
39790     minValue : Number.NEGATIVE_INFINITY,
39791     /**
39792      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39793      */
39794     maxValue : Number.MAX_VALUE,
39795     /**
39796      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39797      */
39798     minText : "The minimum value for this field is {0}",
39799     /**
39800      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39801      */
39802     maxText : "The maximum value for this field is {0}",
39803     /**
39804      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39805      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39806      */
39807     nanText : "{0} is not a valid number",
39808
39809     // private
39810     initEvents : function(){
39811         Roo.form.NumberField.superclass.initEvents.call(this);
39812         var allowed = "0123456789";
39813         if(this.allowDecimals){
39814             allowed += this.decimalSeparator;
39815         }
39816         if(this.allowNegative){
39817             allowed += "-";
39818         }
39819         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39820         var keyPress = function(e){
39821             var k = e.getKey();
39822             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39823                 return;
39824             }
39825             var c = e.getCharCode();
39826             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39827                 e.stopEvent();
39828             }
39829         };
39830         this.el.on("keypress", keyPress, this);
39831     },
39832
39833     // private
39834     validateValue : function(value){
39835         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39836             return false;
39837         }
39838         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39839              return true;
39840         }
39841         var num = this.parseValue(value);
39842         if(isNaN(num)){
39843             this.markInvalid(String.format(this.nanText, value));
39844             return false;
39845         }
39846         if(num < this.minValue){
39847             this.markInvalid(String.format(this.minText, this.minValue));
39848             return false;
39849         }
39850         if(num > this.maxValue){
39851             this.markInvalid(String.format(this.maxText, this.maxValue));
39852             return false;
39853         }
39854         return true;
39855     },
39856
39857     getValue : function(){
39858         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39859     },
39860
39861     // private
39862     parseValue : function(value){
39863         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39864         return isNaN(value) ? '' : value;
39865     },
39866
39867     // private
39868     fixPrecision : function(value){
39869         var nan = isNaN(value);
39870         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39871             return nan ? '' : value;
39872         }
39873         return parseFloat(value).toFixed(this.decimalPrecision);
39874     },
39875
39876     setValue : function(v){
39877         v = this.fixPrecision(v);
39878         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
39879     },
39880
39881     // private
39882     decimalPrecisionFcn : function(v){
39883         return Math.floor(v);
39884     },
39885
39886     beforeBlur : function(){
39887         var v = this.parseValue(this.getRawValue());
39888         if(v){
39889             this.setValue(v);
39890         }
39891     }
39892 });/*
39893  * Based on:
39894  * Ext JS Library 1.1.1
39895  * Copyright(c) 2006-2007, Ext JS, LLC.
39896  *
39897  * Originally Released Under LGPL - original licence link has changed is not relivant.
39898  *
39899  * Fork - LGPL
39900  * <script type="text/javascript">
39901  */
39902  
39903 /**
39904  * @class Roo.form.DateField
39905  * @extends Roo.form.TriggerField
39906  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
39907 * @constructor
39908 * Create a new DateField
39909 * @param {Object} config
39910  */
39911 Roo.form.DateField = function(config){
39912     Roo.form.DateField.superclass.constructor.call(this, config);
39913     
39914       this.addEvents({
39915          
39916         /**
39917          * @event select
39918          * Fires when a date is selected
39919              * @param {Roo.form.DateField} combo This combo box
39920              * @param {Date} date The date selected
39921              */
39922         'select' : true
39923          
39924     });
39925     
39926     
39927     if(typeof this.minValue == "string") {
39928         this.minValue = this.parseDate(this.minValue);
39929     }
39930     if(typeof this.maxValue == "string") {
39931         this.maxValue = this.parseDate(this.maxValue);
39932     }
39933     this.ddMatch = null;
39934     if(this.disabledDates){
39935         var dd = this.disabledDates;
39936         var re = "(?:";
39937         for(var i = 0; i < dd.length; i++){
39938             re += dd[i];
39939             if(i != dd.length-1) {
39940                 re += "|";
39941             }
39942         }
39943         this.ddMatch = new RegExp(re + ")");
39944     }
39945 };
39946
39947 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
39948     /**
39949      * @cfg {String} format
39950      * The default date format string which can be overriden for localization support.  The format must be
39951      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
39952      */
39953     format : "m/d/y",
39954     /**
39955      * @cfg {String} altFormats
39956      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
39957      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
39958      */
39959     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
39960     /**
39961      * @cfg {Array} disabledDays
39962      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
39963      */
39964     disabledDays : null,
39965     /**
39966      * @cfg {String} disabledDaysText
39967      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
39968      */
39969     disabledDaysText : "Disabled",
39970     /**
39971      * @cfg {Array} disabledDates
39972      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
39973      * expression so they are very powerful. Some examples:
39974      * <ul>
39975      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
39976      * <li>["03/08", "09/16"] would disable those days for every year</li>
39977      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
39978      * <li>["03/../2006"] would disable every day in March 2006</li>
39979      * <li>["^03"] would disable every day in every March</li>
39980      * </ul>
39981      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
39982      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
39983      */
39984     disabledDates : null,
39985     /**
39986      * @cfg {String} disabledDatesText
39987      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39988      */
39989     disabledDatesText : "Disabled",
39990     /**
39991      * @cfg {Date/String} minValue
39992      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39993      * valid format (defaults to null).
39994      */
39995     minValue : null,
39996     /**
39997      * @cfg {Date/String} maxValue
39998      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39999      * valid format (defaults to null).
40000      */
40001     maxValue : null,
40002     /**
40003      * @cfg {String} minText
40004      * The error text to display when the date in the cell is before minValue (defaults to
40005      * 'The date in this field must be after {minValue}').
40006      */
40007     minText : "The date in this field must be equal to or after {0}",
40008     /**
40009      * @cfg {String} maxText
40010      * The error text to display when the date in the cell is after maxValue (defaults to
40011      * 'The date in this field must be before {maxValue}').
40012      */
40013     maxText : "The date in this field must be equal to or before {0}",
40014     /**
40015      * @cfg {String} invalidText
40016      * The error text to display when the date in the field is invalid (defaults to
40017      * '{value} is not a valid date - it must be in the format {format}').
40018      */
40019     invalidText : "{0} is not a valid date - it must be in the format {1}",
40020     /**
40021      * @cfg {String} triggerClass
40022      * An additional CSS class used to style the trigger button.  The trigger will always get the
40023      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40024      * which displays a calendar icon).
40025      */
40026     triggerClass : 'x-form-date-trigger',
40027     
40028
40029     /**
40030      * @cfg {Boolean} useIso
40031      * if enabled, then the date field will use a hidden field to store the 
40032      * real value as iso formated date. default (false)
40033      */ 
40034     useIso : false,
40035     /**
40036      * @cfg {String/Object} autoCreate
40037      * A DomHelper element spec, or true for a default element spec (defaults to
40038      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40039      */ 
40040     // private
40041     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40042     
40043     // private
40044     hiddenField: false,
40045     
40046     onRender : function(ct, position)
40047     {
40048         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40049         if (this.useIso) {
40050             //this.el.dom.removeAttribute('name'); 
40051             Roo.log("Changing name?");
40052             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40053             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40054                     'before', true);
40055             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40056             // prevent input submission
40057             this.hiddenName = this.name;
40058         }
40059             
40060             
40061     },
40062     
40063     // private
40064     validateValue : function(value)
40065     {
40066         value = this.formatDate(value);
40067         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40068             Roo.log('super failed');
40069             return false;
40070         }
40071         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40072              return true;
40073         }
40074         var svalue = value;
40075         value = this.parseDate(value);
40076         if(!value){
40077             Roo.log('parse date failed' + svalue);
40078             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40079             return false;
40080         }
40081         var time = value.getTime();
40082         if(this.minValue && time < this.minValue.getTime()){
40083             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40084             return false;
40085         }
40086         if(this.maxValue && time > this.maxValue.getTime()){
40087             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40088             return false;
40089         }
40090         if(this.disabledDays){
40091             var day = value.getDay();
40092             for(var i = 0; i < this.disabledDays.length; i++) {
40093                 if(day === this.disabledDays[i]){
40094                     this.markInvalid(this.disabledDaysText);
40095                     return false;
40096                 }
40097             }
40098         }
40099         var fvalue = this.formatDate(value);
40100         if(this.ddMatch && this.ddMatch.test(fvalue)){
40101             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40102             return false;
40103         }
40104         return true;
40105     },
40106
40107     // private
40108     // Provides logic to override the default TriggerField.validateBlur which just returns true
40109     validateBlur : function(){
40110         return !this.menu || !this.menu.isVisible();
40111     },
40112     
40113     getName: function()
40114     {
40115         // returns hidden if it's set..
40116         if (!this.rendered) {return ''};
40117         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40118         
40119     },
40120
40121     /**
40122      * Returns the current date value of the date field.
40123      * @return {Date} The date value
40124      */
40125     getValue : function(){
40126         
40127         return  this.hiddenField ?
40128                 this.hiddenField.value :
40129                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40130     },
40131
40132     /**
40133      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40134      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40135      * (the default format used is "m/d/y").
40136      * <br />Usage:
40137      * <pre><code>
40138 //All of these calls set the same date value (May 4, 2006)
40139
40140 //Pass a date object:
40141 var dt = new Date('5/4/06');
40142 dateField.setValue(dt);
40143
40144 //Pass a date string (default format):
40145 dateField.setValue('5/4/06');
40146
40147 //Pass a date string (custom format):
40148 dateField.format = 'Y-m-d';
40149 dateField.setValue('2006-5-4');
40150 </code></pre>
40151      * @param {String/Date} date The date or valid date string
40152      */
40153     setValue : function(date){
40154         if (this.hiddenField) {
40155             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40156         }
40157         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40158         // make sure the value field is always stored as a date..
40159         this.value = this.parseDate(date);
40160         
40161         
40162     },
40163
40164     // private
40165     parseDate : function(value){
40166         if(!value || value instanceof Date){
40167             return value;
40168         }
40169         var v = Date.parseDate(value, this.format);
40170          if (!v && this.useIso) {
40171             v = Date.parseDate(value, 'Y-m-d');
40172         }
40173         if(!v && this.altFormats){
40174             if(!this.altFormatsArray){
40175                 this.altFormatsArray = this.altFormats.split("|");
40176             }
40177             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40178                 v = Date.parseDate(value, this.altFormatsArray[i]);
40179             }
40180         }
40181         return v;
40182     },
40183
40184     // private
40185     formatDate : function(date, fmt){
40186         return (!date || !(date instanceof Date)) ?
40187                date : date.dateFormat(fmt || this.format);
40188     },
40189
40190     // private
40191     menuListeners : {
40192         select: function(m, d){
40193             
40194             this.setValue(d);
40195             this.fireEvent('select', this, d);
40196         },
40197         show : function(){ // retain focus styling
40198             this.onFocus();
40199         },
40200         hide : function(){
40201             this.focus.defer(10, this);
40202             var ml = this.menuListeners;
40203             this.menu.un("select", ml.select,  this);
40204             this.menu.un("show", ml.show,  this);
40205             this.menu.un("hide", ml.hide,  this);
40206         }
40207     },
40208
40209     // private
40210     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40211     onTriggerClick : function(){
40212         if(this.disabled){
40213             return;
40214         }
40215         if(this.menu == null){
40216             this.menu = new Roo.menu.DateMenu();
40217         }
40218         Roo.apply(this.menu.picker,  {
40219             showClear: this.allowBlank,
40220             minDate : this.minValue,
40221             maxDate : this.maxValue,
40222             disabledDatesRE : this.ddMatch,
40223             disabledDatesText : this.disabledDatesText,
40224             disabledDays : this.disabledDays,
40225             disabledDaysText : this.disabledDaysText,
40226             format : this.useIso ? 'Y-m-d' : this.format,
40227             minText : String.format(this.minText, this.formatDate(this.minValue)),
40228             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40229         });
40230         this.menu.on(Roo.apply({}, this.menuListeners, {
40231             scope:this
40232         }));
40233         this.menu.picker.setValue(this.getValue() || new Date());
40234         this.menu.show(this.el, "tl-bl?");
40235     },
40236
40237     beforeBlur : function(){
40238         var v = this.parseDate(this.getRawValue());
40239         if(v){
40240             this.setValue(v);
40241         }
40242     },
40243
40244     /*@
40245      * overide
40246      * 
40247      */
40248     isDirty : function() {
40249         if(this.disabled) {
40250             return false;
40251         }
40252         
40253         if(typeof(this.startValue) === 'undefined'){
40254             return false;
40255         }
40256         
40257         return String(this.getValue()) !== String(this.startValue);
40258         
40259     }
40260 });/*
40261  * Based on:
40262  * Ext JS Library 1.1.1
40263  * Copyright(c) 2006-2007, Ext JS, LLC.
40264  *
40265  * Originally Released Under LGPL - original licence link has changed is not relivant.
40266  *
40267  * Fork - LGPL
40268  * <script type="text/javascript">
40269  */
40270  
40271 /**
40272  * @class Roo.form.MonthField
40273  * @extends Roo.form.TriggerField
40274  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40275 * @constructor
40276 * Create a new MonthField
40277 * @param {Object} config
40278  */
40279 Roo.form.MonthField = function(config){
40280     
40281     Roo.form.MonthField.superclass.constructor.call(this, config);
40282     
40283       this.addEvents({
40284          
40285         /**
40286          * @event select
40287          * Fires when a date is selected
40288              * @param {Roo.form.MonthFieeld} combo This combo box
40289              * @param {Date} date The date selected
40290              */
40291         'select' : true
40292          
40293     });
40294     
40295     
40296     if(typeof this.minValue == "string") {
40297         this.minValue = this.parseDate(this.minValue);
40298     }
40299     if(typeof this.maxValue == "string") {
40300         this.maxValue = this.parseDate(this.maxValue);
40301     }
40302     this.ddMatch = null;
40303     if(this.disabledDates){
40304         var dd = this.disabledDates;
40305         var re = "(?:";
40306         for(var i = 0; i < dd.length; i++){
40307             re += dd[i];
40308             if(i != dd.length-1) {
40309                 re += "|";
40310             }
40311         }
40312         this.ddMatch = new RegExp(re + ")");
40313     }
40314 };
40315
40316 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40317     /**
40318      * @cfg {String} format
40319      * The default date format string which can be overriden for localization support.  The format must be
40320      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40321      */
40322     format : "M Y",
40323     /**
40324      * @cfg {String} altFormats
40325      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40326      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40327      */
40328     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40329     /**
40330      * @cfg {Array} disabledDays
40331      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40332      */
40333     disabledDays : [0,1,2,3,4,5,6],
40334     /**
40335      * @cfg {String} disabledDaysText
40336      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40337      */
40338     disabledDaysText : "Disabled",
40339     /**
40340      * @cfg {Array} disabledDates
40341      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40342      * expression so they are very powerful. Some examples:
40343      * <ul>
40344      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40345      * <li>["03/08", "09/16"] would disable those days for every year</li>
40346      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40347      * <li>["03/../2006"] would disable every day in March 2006</li>
40348      * <li>["^03"] would disable every day in every March</li>
40349      * </ul>
40350      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40351      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40352      */
40353     disabledDates : null,
40354     /**
40355      * @cfg {String} disabledDatesText
40356      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40357      */
40358     disabledDatesText : "Disabled",
40359     /**
40360      * @cfg {Date/String} minValue
40361      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40362      * valid format (defaults to null).
40363      */
40364     minValue : null,
40365     /**
40366      * @cfg {Date/String} maxValue
40367      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40368      * valid format (defaults to null).
40369      */
40370     maxValue : null,
40371     /**
40372      * @cfg {String} minText
40373      * The error text to display when the date in the cell is before minValue (defaults to
40374      * 'The date in this field must be after {minValue}').
40375      */
40376     minText : "The date in this field must be equal to or after {0}",
40377     /**
40378      * @cfg {String} maxTextf
40379      * The error text to display when the date in the cell is after maxValue (defaults to
40380      * 'The date in this field must be before {maxValue}').
40381      */
40382     maxText : "The date in this field must be equal to or before {0}",
40383     /**
40384      * @cfg {String} invalidText
40385      * The error text to display when the date in the field is invalid (defaults to
40386      * '{value} is not a valid date - it must be in the format {format}').
40387      */
40388     invalidText : "{0} is not a valid date - it must be in the format {1}",
40389     /**
40390      * @cfg {String} triggerClass
40391      * An additional CSS class used to style the trigger button.  The trigger will always get the
40392      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40393      * which displays a calendar icon).
40394      */
40395     triggerClass : 'x-form-date-trigger',
40396     
40397
40398     /**
40399      * @cfg {Boolean} useIso
40400      * if enabled, then the date field will use a hidden field to store the 
40401      * real value as iso formated date. default (true)
40402      */ 
40403     useIso : true,
40404     /**
40405      * @cfg {String/Object} autoCreate
40406      * A DomHelper element spec, or true for a default element spec (defaults to
40407      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40408      */ 
40409     // private
40410     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40411     
40412     // private
40413     hiddenField: false,
40414     
40415     hideMonthPicker : false,
40416     
40417     onRender : function(ct, position)
40418     {
40419         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40420         if (this.useIso) {
40421             this.el.dom.removeAttribute('name'); 
40422             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40423                     'before', true);
40424             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40425             // prevent input submission
40426             this.hiddenName = this.name;
40427         }
40428             
40429             
40430     },
40431     
40432     // private
40433     validateValue : function(value)
40434     {
40435         value = this.formatDate(value);
40436         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40437             return false;
40438         }
40439         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40440              return true;
40441         }
40442         var svalue = value;
40443         value = this.parseDate(value);
40444         if(!value){
40445             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40446             return false;
40447         }
40448         var time = value.getTime();
40449         if(this.minValue && time < this.minValue.getTime()){
40450             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40451             return false;
40452         }
40453         if(this.maxValue && time > this.maxValue.getTime()){
40454             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40455             return false;
40456         }
40457         /*if(this.disabledDays){
40458             var day = value.getDay();
40459             for(var i = 0; i < this.disabledDays.length; i++) {
40460                 if(day === this.disabledDays[i]){
40461                     this.markInvalid(this.disabledDaysText);
40462                     return false;
40463                 }
40464             }
40465         }
40466         */
40467         var fvalue = this.formatDate(value);
40468         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40469             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40470             return false;
40471         }
40472         */
40473         return true;
40474     },
40475
40476     // private
40477     // Provides logic to override the default TriggerField.validateBlur which just returns true
40478     validateBlur : function(){
40479         return !this.menu || !this.menu.isVisible();
40480     },
40481
40482     /**
40483      * Returns the current date value of the date field.
40484      * @return {Date} The date value
40485      */
40486     getValue : function(){
40487         
40488         
40489         
40490         return  this.hiddenField ?
40491                 this.hiddenField.value :
40492                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40493     },
40494
40495     /**
40496      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40497      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40498      * (the default format used is "m/d/y").
40499      * <br />Usage:
40500      * <pre><code>
40501 //All of these calls set the same date value (May 4, 2006)
40502
40503 //Pass a date object:
40504 var dt = new Date('5/4/06');
40505 monthField.setValue(dt);
40506
40507 //Pass a date string (default format):
40508 monthField.setValue('5/4/06');
40509
40510 //Pass a date string (custom format):
40511 monthField.format = 'Y-m-d';
40512 monthField.setValue('2006-5-4');
40513 </code></pre>
40514      * @param {String/Date} date The date or valid date string
40515      */
40516     setValue : function(date){
40517         Roo.log('month setValue' + date);
40518         // can only be first of month..
40519         
40520         var val = this.parseDate(date);
40521         
40522         if (this.hiddenField) {
40523             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40524         }
40525         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40526         this.value = this.parseDate(date);
40527     },
40528
40529     // private
40530     parseDate : function(value){
40531         if(!value || value instanceof Date){
40532             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40533             return value;
40534         }
40535         var v = Date.parseDate(value, this.format);
40536         if (!v && this.useIso) {
40537             v = Date.parseDate(value, 'Y-m-d');
40538         }
40539         if (v) {
40540             // 
40541             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40542         }
40543         
40544         
40545         if(!v && this.altFormats){
40546             if(!this.altFormatsArray){
40547                 this.altFormatsArray = this.altFormats.split("|");
40548             }
40549             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40550                 v = Date.parseDate(value, this.altFormatsArray[i]);
40551             }
40552         }
40553         return v;
40554     },
40555
40556     // private
40557     formatDate : function(date, fmt){
40558         return (!date || !(date instanceof Date)) ?
40559                date : date.dateFormat(fmt || this.format);
40560     },
40561
40562     // private
40563     menuListeners : {
40564         select: function(m, d){
40565             this.setValue(d);
40566             this.fireEvent('select', this, d);
40567         },
40568         show : function(){ // retain focus styling
40569             this.onFocus();
40570         },
40571         hide : function(){
40572             this.focus.defer(10, this);
40573             var ml = this.menuListeners;
40574             this.menu.un("select", ml.select,  this);
40575             this.menu.un("show", ml.show,  this);
40576             this.menu.un("hide", ml.hide,  this);
40577         }
40578     },
40579     // private
40580     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40581     onTriggerClick : function(){
40582         if(this.disabled){
40583             return;
40584         }
40585         if(this.menu == null){
40586             this.menu = new Roo.menu.DateMenu();
40587            
40588         }
40589         
40590         Roo.apply(this.menu.picker,  {
40591             
40592             showClear: this.allowBlank,
40593             minDate : this.minValue,
40594             maxDate : this.maxValue,
40595             disabledDatesRE : this.ddMatch,
40596             disabledDatesText : this.disabledDatesText,
40597             
40598             format : this.useIso ? 'Y-m-d' : this.format,
40599             minText : String.format(this.minText, this.formatDate(this.minValue)),
40600             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40601             
40602         });
40603          this.menu.on(Roo.apply({}, this.menuListeners, {
40604             scope:this
40605         }));
40606        
40607         
40608         var m = this.menu;
40609         var p = m.picker;
40610         
40611         // hide month picker get's called when we called by 'before hide';
40612         
40613         var ignorehide = true;
40614         p.hideMonthPicker  = function(disableAnim){
40615             if (ignorehide) {
40616                 return;
40617             }
40618              if(this.monthPicker){
40619                 Roo.log("hideMonthPicker called");
40620                 if(disableAnim === true){
40621                     this.monthPicker.hide();
40622                 }else{
40623                     this.monthPicker.slideOut('t', {duration:.2});
40624                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40625                     p.fireEvent("select", this, this.value);
40626                     m.hide();
40627                 }
40628             }
40629         }
40630         
40631         Roo.log('picker set value');
40632         Roo.log(this.getValue());
40633         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40634         m.show(this.el, 'tl-bl?');
40635         ignorehide  = false;
40636         // this will trigger hideMonthPicker..
40637         
40638         
40639         // hidden the day picker
40640         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40641         
40642         
40643         
40644       
40645         
40646         p.showMonthPicker.defer(100, p);
40647     
40648         
40649        
40650     },
40651
40652     beforeBlur : function(){
40653         var v = this.parseDate(this.getRawValue());
40654         if(v){
40655             this.setValue(v);
40656         }
40657     }
40658
40659     /** @cfg {Boolean} grow @hide */
40660     /** @cfg {Number} growMin @hide */
40661     /** @cfg {Number} growMax @hide */
40662     /**
40663      * @hide
40664      * @method autoSize
40665      */
40666 });/*
40667  * Based on:
40668  * Ext JS Library 1.1.1
40669  * Copyright(c) 2006-2007, Ext JS, LLC.
40670  *
40671  * Originally Released Under LGPL - original licence link has changed is not relivant.
40672  *
40673  * Fork - LGPL
40674  * <script type="text/javascript">
40675  */
40676  
40677
40678 /**
40679  * @class Roo.form.ComboBox
40680  * @extends Roo.form.TriggerField
40681  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40682  * @constructor
40683  * Create a new ComboBox.
40684  * @param {Object} config Configuration options
40685  */
40686 Roo.form.ComboBox = function(config){
40687     Roo.form.ComboBox.superclass.constructor.call(this, config);
40688     this.addEvents({
40689         /**
40690          * @event expand
40691          * Fires when the dropdown list is expanded
40692              * @param {Roo.form.ComboBox} combo This combo box
40693              */
40694         'expand' : true,
40695         /**
40696          * @event collapse
40697          * Fires when the dropdown list is collapsed
40698              * @param {Roo.form.ComboBox} combo This combo box
40699              */
40700         'collapse' : true,
40701         /**
40702          * @event beforeselect
40703          * Fires before a list item is selected. Return false to cancel the selection.
40704              * @param {Roo.form.ComboBox} combo This combo box
40705              * @param {Roo.data.Record} record The data record returned from the underlying store
40706              * @param {Number} index The index of the selected item in the dropdown list
40707              */
40708         'beforeselect' : true,
40709         /**
40710          * @event select
40711          * Fires when a list item is selected
40712              * @param {Roo.form.ComboBox} combo This combo box
40713              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40714              * @param {Number} index The index of the selected item in the dropdown list
40715              */
40716         'select' : true,
40717         /**
40718          * @event beforequery
40719          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40720          * The event object passed has these properties:
40721              * @param {Roo.form.ComboBox} combo This combo box
40722              * @param {String} query The query
40723              * @param {Boolean} forceAll true to force "all" query
40724              * @param {Boolean} cancel true to cancel the query
40725              * @param {Object} e The query event object
40726              */
40727         'beforequery': true,
40728          /**
40729          * @event add
40730          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40731              * @param {Roo.form.ComboBox} combo This combo box
40732              */
40733         'add' : true,
40734         /**
40735          * @event edit
40736          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40737              * @param {Roo.form.ComboBox} combo This combo box
40738              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40739              */
40740         'edit' : true
40741         
40742         
40743     });
40744     if(this.transform){
40745         this.allowDomMove = false;
40746         var s = Roo.getDom(this.transform);
40747         if(!this.hiddenName){
40748             this.hiddenName = s.name;
40749         }
40750         if(!this.store){
40751             this.mode = 'local';
40752             var d = [], opts = s.options;
40753             for(var i = 0, len = opts.length;i < len; i++){
40754                 var o = opts[i];
40755                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40756                 if(o.selected) {
40757                     this.value = value;
40758                 }
40759                 d.push([value, o.text]);
40760             }
40761             this.store = new Roo.data.SimpleStore({
40762                 'id': 0,
40763                 fields: ['value', 'text'],
40764                 data : d
40765             });
40766             this.valueField = 'value';
40767             this.displayField = 'text';
40768         }
40769         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40770         if(!this.lazyRender){
40771             this.target = true;
40772             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40773             s.parentNode.removeChild(s); // remove it
40774             this.render(this.el.parentNode);
40775         }else{
40776             s.parentNode.removeChild(s); // remove it
40777         }
40778
40779     }
40780     if (this.store) {
40781         this.store = Roo.factory(this.store, Roo.data);
40782     }
40783     
40784     this.selectedIndex = -1;
40785     if(this.mode == 'local'){
40786         if(config.queryDelay === undefined){
40787             this.queryDelay = 10;
40788         }
40789         if(config.minChars === undefined){
40790             this.minChars = 0;
40791         }
40792     }
40793 };
40794
40795 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40796     /**
40797      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40798      */
40799     /**
40800      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40801      * rendering into an Roo.Editor, defaults to false)
40802      */
40803     /**
40804      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40805      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40806      */
40807     /**
40808      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40809      */
40810     /**
40811      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40812      * the dropdown list (defaults to undefined, with no header element)
40813      */
40814
40815      /**
40816      * @cfg {String/Roo.Template} tpl The template to use to render the output
40817      */
40818      
40819     // private
40820     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40821     /**
40822      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40823      */
40824     listWidth: undefined,
40825     /**
40826      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40827      * mode = 'remote' or 'text' if mode = 'local')
40828      */
40829     displayField: undefined,
40830     /**
40831      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40832      * mode = 'remote' or 'value' if mode = 'local'). 
40833      * Note: use of a valueField requires the user make a selection
40834      * in order for a value to be mapped.
40835      */
40836     valueField: undefined,
40837     
40838     
40839     /**
40840      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40841      * field's data value (defaults to the underlying DOM element's name)
40842      */
40843     hiddenName: undefined,
40844     /**
40845      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40846      */
40847     listClass: '',
40848     /**
40849      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40850      */
40851     selectedClass: 'x-combo-selected',
40852     /**
40853      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40854      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40855      * which displays a downward arrow icon).
40856      */
40857     triggerClass : 'x-form-arrow-trigger',
40858     /**
40859      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40860      */
40861     shadow:'sides',
40862     /**
40863      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
40864      * anchor positions (defaults to 'tl-bl')
40865      */
40866     listAlign: 'tl-bl?',
40867     /**
40868      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
40869      */
40870     maxHeight: 300,
40871     /**
40872      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
40873      * query specified by the allQuery config option (defaults to 'query')
40874      */
40875     triggerAction: 'query',
40876     /**
40877      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
40878      * (defaults to 4, does not apply if editable = false)
40879      */
40880     minChars : 4,
40881     /**
40882      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
40883      * delay (typeAheadDelay) if it matches a known value (defaults to false)
40884      */
40885     typeAhead: false,
40886     /**
40887      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
40888      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
40889      */
40890     queryDelay: 500,
40891     /**
40892      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
40893      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
40894      */
40895     pageSize: 0,
40896     /**
40897      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
40898      * when editable = true (defaults to false)
40899      */
40900     selectOnFocus:false,
40901     /**
40902      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
40903      */
40904     queryParam: 'query',
40905     /**
40906      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
40907      * when mode = 'remote' (defaults to 'Loading...')
40908      */
40909     loadingText: 'Loading...',
40910     /**
40911      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
40912      */
40913     resizable: false,
40914     /**
40915      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
40916      */
40917     handleHeight : 8,
40918     /**
40919      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
40920      * traditional select (defaults to true)
40921      */
40922     editable: true,
40923     /**
40924      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
40925      */
40926     allQuery: '',
40927     /**
40928      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
40929      */
40930     mode: 'remote',
40931     /**
40932      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
40933      * listWidth has a higher value)
40934      */
40935     minListWidth : 70,
40936     /**
40937      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
40938      * allow the user to set arbitrary text into the field (defaults to false)
40939      */
40940     forceSelection:false,
40941     /**
40942      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
40943      * if typeAhead = true (defaults to 250)
40944      */
40945     typeAheadDelay : 250,
40946     /**
40947      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
40948      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
40949      */
40950     valueNotFoundText : undefined,
40951     /**
40952      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
40953      */
40954     blockFocus : false,
40955     
40956     /**
40957      * @cfg {Boolean} disableClear Disable showing of clear button.
40958      */
40959     disableClear : false,
40960     /**
40961      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
40962      */
40963     alwaysQuery : false,
40964     
40965     //private
40966     addicon : false,
40967     editicon: false,
40968     
40969     // element that contains real text value.. (when hidden is used..)
40970      
40971     // private
40972     onRender : function(ct, position){
40973         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
40974         if(this.hiddenName){
40975             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
40976                     'before', true);
40977             this.hiddenField.value =
40978                 this.hiddenValue !== undefined ? this.hiddenValue :
40979                 this.value !== undefined ? this.value : '';
40980
40981             // prevent input submission
40982             this.el.dom.removeAttribute('name');
40983              
40984              
40985         }
40986         if(Roo.isGecko){
40987             this.el.dom.setAttribute('autocomplete', 'off');
40988         }
40989
40990         var cls = 'x-combo-list';
40991
40992         this.list = new Roo.Layer({
40993             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
40994         });
40995
40996         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
40997         this.list.setWidth(lw);
40998         this.list.swallowEvent('mousewheel');
40999         this.assetHeight = 0;
41000
41001         if(this.title){
41002             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41003             this.assetHeight += this.header.getHeight();
41004         }
41005
41006         this.innerList = this.list.createChild({cls:cls+'-inner'});
41007         this.innerList.on('mouseover', this.onViewOver, this);
41008         this.innerList.on('mousemove', this.onViewMove, this);
41009         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41010         
41011         if(this.allowBlank && !this.pageSize && !this.disableClear){
41012             this.footer = this.list.createChild({cls:cls+'-ft'});
41013             this.pageTb = new Roo.Toolbar(this.footer);
41014            
41015         }
41016         if(this.pageSize){
41017             this.footer = this.list.createChild({cls:cls+'-ft'});
41018             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41019                     {pageSize: this.pageSize});
41020             
41021         }
41022         
41023         if (this.pageTb && this.allowBlank && !this.disableClear) {
41024             var _this = this;
41025             this.pageTb.add(new Roo.Toolbar.Fill(), {
41026                 cls: 'x-btn-icon x-btn-clear',
41027                 text: '&#160;',
41028                 handler: function()
41029                 {
41030                     _this.collapse();
41031                     _this.clearValue();
41032                     _this.onSelect(false, -1);
41033                 }
41034             });
41035         }
41036         if (this.footer) {
41037             this.assetHeight += this.footer.getHeight();
41038         }
41039         
41040
41041         if(!this.tpl){
41042             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41043         }
41044
41045         this.view = new Roo.View(this.innerList, this.tpl, {
41046             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41047         });
41048
41049         this.view.on('click', this.onViewClick, this);
41050
41051         this.store.on('beforeload', this.onBeforeLoad, this);
41052         this.store.on('load', this.onLoad, this);
41053         this.store.on('loadexception', this.onLoadException, this);
41054
41055         if(this.resizable){
41056             this.resizer = new Roo.Resizable(this.list,  {
41057                pinned:true, handles:'se'
41058             });
41059             this.resizer.on('resize', function(r, w, h){
41060                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41061                 this.listWidth = w;
41062                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41063                 this.restrictHeight();
41064             }, this);
41065             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41066         }
41067         if(!this.editable){
41068             this.editable = true;
41069             this.setEditable(false);
41070         }  
41071         
41072         
41073         if (typeof(this.events.add.listeners) != 'undefined') {
41074             
41075             this.addicon = this.wrap.createChild(
41076                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41077        
41078             this.addicon.on('click', function(e) {
41079                 this.fireEvent('add', this);
41080             }, this);
41081         }
41082         if (typeof(this.events.edit.listeners) != 'undefined') {
41083             
41084             this.editicon = this.wrap.createChild(
41085                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41086             if (this.addicon) {
41087                 this.editicon.setStyle('margin-left', '40px');
41088             }
41089             this.editicon.on('click', function(e) {
41090                 
41091                 // we fire even  if inothing is selected..
41092                 this.fireEvent('edit', this, this.lastData );
41093                 
41094             }, this);
41095         }
41096         
41097         
41098         
41099     },
41100
41101     // private
41102     initEvents : function(){
41103         Roo.form.ComboBox.superclass.initEvents.call(this);
41104
41105         this.keyNav = new Roo.KeyNav(this.el, {
41106             "up" : function(e){
41107                 this.inKeyMode = true;
41108                 this.selectPrev();
41109             },
41110
41111             "down" : function(e){
41112                 if(!this.isExpanded()){
41113                     this.onTriggerClick();
41114                 }else{
41115                     this.inKeyMode = true;
41116                     this.selectNext();
41117                 }
41118             },
41119
41120             "enter" : function(e){
41121                 this.onViewClick();
41122                 //return true;
41123             },
41124
41125             "esc" : function(e){
41126                 this.collapse();
41127             },
41128
41129             "tab" : function(e){
41130                 this.onViewClick(false);
41131                 this.fireEvent("specialkey", this, e);
41132                 return true;
41133             },
41134
41135             scope : this,
41136
41137             doRelay : function(foo, bar, hname){
41138                 if(hname == 'down' || this.scope.isExpanded()){
41139                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41140                 }
41141                 return true;
41142             },
41143
41144             forceKeyDown: true
41145         });
41146         this.queryDelay = Math.max(this.queryDelay || 10,
41147                 this.mode == 'local' ? 10 : 250);
41148         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41149         if(this.typeAhead){
41150             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41151         }
41152         if(this.editable !== false){
41153             this.el.on("keyup", this.onKeyUp, this);
41154         }
41155         if(this.forceSelection){
41156             this.on('blur', this.doForce, this);
41157         }
41158     },
41159
41160     onDestroy : function(){
41161         if(this.view){
41162             this.view.setStore(null);
41163             this.view.el.removeAllListeners();
41164             this.view.el.remove();
41165             this.view.purgeListeners();
41166         }
41167         if(this.list){
41168             this.list.destroy();
41169         }
41170         if(this.store){
41171             this.store.un('beforeload', this.onBeforeLoad, this);
41172             this.store.un('load', this.onLoad, this);
41173             this.store.un('loadexception', this.onLoadException, this);
41174         }
41175         Roo.form.ComboBox.superclass.onDestroy.call(this);
41176     },
41177
41178     // private
41179     fireKey : function(e){
41180         if(e.isNavKeyPress() && !this.list.isVisible()){
41181             this.fireEvent("specialkey", this, e);
41182         }
41183     },
41184
41185     // private
41186     onResize: function(w, h){
41187         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41188         
41189         if(typeof w != 'number'){
41190             // we do not handle it!?!?
41191             return;
41192         }
41193         var tw = this.trigger.getWidth();
41194         tw += this.addicon ? this.addicon.getWidth() : 0;
41195         tw += this.editicon ? this.editicon.getWidth() : 0;
41196         var x = w - tw;
41197         this.el.setWidth( this.adjustWidth('input', x));
41198             
41199         this.trigger.setStyle('left', x+'px');
41200         
41201         if(this.list && this.listWidth === undefined){
41202             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41203             this.list.setWidth(lw);
41204             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41205         }
41206         
41207     
41208         
41209     },
41210
41211     /**
41212      * Allow or prevent the user from directly editing the field text.  If false is passed,
41213      * the user will only be able to select from the items defined in the dropdown list.  This method
41214      * is the runtime equivalent of setting the 'editable' config option at config time.
41215      * @param {Boolean} value True to allow the user to directly edit the field text
41216      */
41217     setEditable : function(value){
41218         if(value == this.editable){
41219             return;
41220         }
41221         this.editable = value;
41222         if(!value){
41223             this.el.dom.setAttribute('readOnly', true);
41224             this.el.on('mousedown', this.onTriggerClick,  this);
41225             this.el.addClass('x-combo-noedit');
41226         }else{
41227             this.el.dom.setAttribute('readOnly', false);
41228             this.el.un('mousedown', this.onTriggerClick,  this);
41229             this.el.removeClass('x-combo-noedit');
41230         }
41231     },
41232
41233     // private
41234     onBeforeLoad : function(){
41235         if(!this.hasFocus){
41236             return;
41237         }
41238         this.innerList.update(this.loadingText ?
41239                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41240         this.restrictHeight();
41241         this.selectedIndex = -1;
41242     },
41243
41244     // private
41245     onLoad : function(){
41246         if(!this.hasFocus){
41247             return;
41248         }
41249         if(this.store.getCount() > 0){
41250             this.expand();
41251             this.restrictHeight();
41252             if(this.lastQuery == this.allQuery){
41253                 if(this.editable){
41254                     this.el.dom.select();
41255                 }
41256                 if(!this.selectByValue(this.value, true)){
41257                     this.select(0, true);
41258                 }
41259             }else{
41260                 this.selectNext();
41261                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41262                     this.taTask.delay(this.typeAheadDelay);
41263                 }
41264             }
41265         }else{
41266             this.onEmptyResults();
41267         }
41268         //this.el.focus();
41269     },
41270     // private
41271     onLoadException : function()
41272     {
41273         this.collapse();
41274         Roo.log(this.store.reader.jsonData);
41275         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41276             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41277         }
41278         
41279         
41280     },
41281     // private
41282     onTypeAhead : function(){
41283         if(this.store.getCount() > 0){
41284             var r = this.store.getAt(0);
41285             var newValue = r.data[this.displayField];
41286             var len = newValue.length;
41287             var selStart = this.getRawValue().length;
41288             if(selStart != len){
41289                 this.setRawValue(newValue);
41290                 this.selectText(selStart, newValue.length);
41291             }
41292         }
41293     },
41294
41295     // private
41296     onSelect : function(record, index){
41297         if(this.fireEvent('beforeselect', this, record, index) !== false){
41298             this.setFromData(index > -1 ? record.data : false);
41299             this.collapse();
41300             this.fireEvent('select', this, record, index);
41301         }
41302     },
41303
41304     /**
41305      * Returns the currently selected field value or empty string if no value is set.
41306      * @return {String} value The selected value
41307      */
41308     getValue : function(){
41309         if(this.valueField){
41310             return typeof this.value != 'undefined' ? this.value : '';
41311         }
41312         return Roo.form.ComboBox.superclass.getValue.call(this);
41313     },
41314
41315     /**
41316      * Clears any text/value currently set in the field
41317      */
41318     clearValue : function(){
41319         if(this.hiddenField){
41320             this.hiddenField.value = '';
41321         }
41322         this.value = '';
41323         this.setRawValue('');
41324         this.lastSelectionText = '';
41325         
41326     },
41327
41328     /**
41329      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41330      * will be displayed in the field.  If the value does not match the data value of an existing item,
41331      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41332      * Otherwise the field will be blank (although the value will still be set).
41333      * @param {String} value The value to match
41334      */
41335     setValue : function(v){
41336         var text = v;
41337         if(this.valueField){
41338             var r = this.findRecord(this.valueField, v);
41339             if(r){
41340                 text = r.data[this.displayField];
41341             }else if(this.valueNotFoundText !== undefined){
41342                 text = this.valueNotFoundText;
41343             }
41344         }
41345         this.lastSelectionText = text;
41346         if(this.hiddenField){
41347             this.hiddenField.value = v;
41348         }
41349         Roo.form.ComboBox.superclass.setValue.call(this, text);
41350         this.value = v;
41351     },
41352     /**
41353      * @property {Object} the last set data for the element
41354      */
41355     
41356     lastData : false,
41357     /**
41358      * Sets the value of the field based on a object which is related to the record format for the store.
41359      * @param {Object} value the value to set as. or false on reset?
41360      */
41361     setFromData : function(o){
41362         var dv = ''; // display value
41363         var vv = ''; // value value..
41364         this.lastData = o;
41365         if (this.displayField) {
41366             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41367         } else {
41368             // this is an error condition!!!
41369             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41370         }
41371         
41372         if(this.valueField){
41373             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41374         }
41375         if(this.hiddenField){
41376             this.hiddenField.value = vv;
41377             
41378             this.lastSelectionText = dv;
41379             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41380             this.value = vv;
41381             return;
41382         }
41383         // no hidden field.. - we store the value in 'value', but still display
41384         // display field!!!!
41385         this.lastSelectionText = dv;
41386         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41387         this.value = vv;
41388         
41389         
41390     },
41391     // private
41392     reset : function(){
41393         // overridden so that last data is reset..
41394         this.setValue(this.resetValue);
41395         this.clearInvalid();
41396         this.lastData = false;
41397         if (this.view) {
41398             this.view.clearSelections();
41399         }
41400     },
41401     // private
41402     findRecord : function(prop, value){
41403         var record;
41404         if(this.store.getCount() > 0){
41405             this.store.each(function(r){
41406                 if(r.data[prop] == value){
41407                     record = r;
41408                     return false;
41409                 }
41410                 return true;
41411             });
41412         }
41413         return record;
41414     },
41415     
41416     getName: function()
41417     {
41418         // returns hidden if it's set..
41419         if (!this.rendered) {return ''};
41420         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41421         
41422     },
41423     // private
41424     onViewMove : function(e, t){
41425         this.inKeyMode = false;
41426     },
41427
41428     // private
41429     onViewOver : function(e, t){
41430         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41431             return;
41432         }
41433         var item = this.view.findItemFromChild(t);
41434         if(item){
41435             var index = this.view.indexOf(item);
41436             this.select(index, false);
41437         }
41438     },
41439
41440     // private
41441     onViewClick : function(doFocus)
41442     {
41443         var index = this.view.getSelectedIndexes()[0];
41444         var r = this.store.getAt(index);
41445         if(r){
41446             this.onSelect(r, index);
41447         }
41448         if(doFocus !== false && !this.blockFocus){
41449             this.el.focus();
41450         }
41451     },
41452
41453     // private
41454     restrictHeight : function(){
41455         this.innerList.dom.style.height = '';
41456         var inner = this.innerList.dom;
41457         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41458         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41459         this.list.beginUpdate();
41460         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41461         this.list.alignTo(this.el, this.listAlign);
41462         this.list.endUpdate();
41463     },
41464
41465     // private
41466     onEmptyResults : function(){
41467         this.collapse();
41468     },
41469
41470     /**
41471      * Returns true if the dropdown list is expanded, else false.
41472      */
41473     isExpanded : function(){
41474         return this.list.isVisible();
41475     },
41476
41477     /**
41478      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41479      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41480      * @param {String} value The data value of the item to select
41481      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41482      * selected item if it is not currently in view (defaults to true)
41483      * @return {Boolean} True if the value matched an item in the list, else false
41484      */
41485     selectByValue : function(v, scrollIntoView){
41486         if(v !== undefined && v !== null){
41487             var r = this.findRecord(this.valueField || this.displayField, v);
41488             if(r){
41489                 this.select(this.store.indexOf(r), scrollIntoView);
41490                 return true;
41491             }
41492         }
41493         return false;
41494     },
41495
41496     /**
41497      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41498      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41499      * @param {Number} index The zero-based index of the list item to select
41500      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41501      * selected item if it is not currently in view (defaults to true)
41502      */
41503     select : function(index, scrollIntoView){
41504         this.selectedIndex = index;
41505         this.view.select(index);
41506         if(scrollIntoView !== false){
41507             var el = this.view.getNode(index);
41508             if(el){
41509                 this.innerList.scrollChildIntoView(el, false);
41510             }
41511         }
41512     },
41513
41514     // private
41515     selectNext : function(){
41516         var ct = this.store.getCount();
41517         if(ct > 0){
41518             if(this.selectedIndex == -1){
41519                 this.select(0);
41520             }else if(this.selectedIndex < ct-1){
41521                 this.select(this.selectedIndex+1);
41522             }
41523         }
41524     },
41525
41526     // private
41527     selectPrev : function(){
41528         var ct = this.store.getCount();
41529         if(ct > 0){
41530             if(this.selectedIndex == -1){
41531                 this.select(0);
41532             }else if(this.selectedIndex != 0){
41533                 this.select(this.selectedIndex-1);
41534             }
41535         }
41536     },
41537
41538     // private
41539     onKeyUp : function(e){
41540         if(this.editable !== false && !e.isSpecialKey()){
41541             this.lastKey = e.getKey();
41542             this.dqTask.delay(this.queryDelay);
41543         }
41544     },
41545
41546     // private
41547     validateBlur : function(){
41548         return !this.list || !this.list.isVisible();   
41549     },
41550
41551     // private
41552     initQuery : function(){
41553         this.doQuery(this.getRawValue());
41554     },
41555
41556     // private
41557     doForce : function(){
41558         if(this.el.dom.value.length > 0){
41559             this.el.dom.value =
41560                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41561              
41562         }
41563     },
41564
41565     /**
41566      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41567      * query allowing the query action to be canceled if needed.
41568      * @param {String} query The SQL query to execute
41569      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41570      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41571      * saved in the current store (defaults to false)
41572      */
41573     doQuery : function(q, forceAll){
41574         if(q === undefined || q === null){
41575             q = '';
41576         }
41577         var qe = {
41578             query: q,
41579             forceAll: forceAll,
41580             combo: this,
41581             cancel:false
41582         };
41583         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41584             return false;
41585         }
41586         q = qe.query;
41587         forceAll = qe.forceAll;
41588         if(forceAll === true || (q.length >= this.minChars)){
41589             if(this.lastQuery != q || this.alwaysQuery){
41590                 this.lastQuery = q;
41591                 if(this.mode == 'local'){
41592                     this.selectedIndex = -1;
41593                     if(forceAll){
41594                         this.store.clearFilter();
41595                     }else{
41596                         this.store.filter(this.displayField, q);
41597                     }
41598                     this.onLoad();
41599                 }else{
41600                     this.store.baseParams[this.queryParam] = q;
41601                     this.store.load({
41602                         params: this.getParams(q)
41603                     });
41604                     this.expand();
41605                 }
41606             }else{
41607                 this.selectedIndex = -1;
41608                 this.onLoad();   
41609             }
41610         }
41611     },
41612
41613     // private
41614     getParams : function(q){
41615         var p = {};
41616         //p[this.queryParam] = q;
41617         if(this.pageSize){
41618             p.start = 0;
41619             p.limit = this.pageSize;
41620         }
41621         return p;
41622     },
41623
41624     /**
41625      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41626      */
41627     collapse : function(){
41628         if(!this.isExpanded()){
41629             return;
41630         }
41631         this.list.hide();
41632         Roo.get(document).un('mousedown', this.collapseIf, this);
41633         Roo.get(document).un('mousewheel', this.collapseIf, this);
41634         if (!this.editable) {
41635             Roo.get(document).un('keydown', this.listKeyPress, this);
41636         }
41637         this.fireEvent('collapse', this);
41638     },
41639
41640     // private
41641     collapseIf : function(e){
41642         if(!e.within(this.wrap) && !e.within(this.list)){
41643             this.collapse();
41644         }
41645     },
41646
41647     /**
41648      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41649      */
41650     expand : function(){
41651         if(this.isExpanded() || !this.hasFocus){
41652             return;
41653         }
41654         this.list.alignTo(this.el, this.listAlign);
41655         this.list.show();
41656         Roo.get(document).on('mousedown', this.collapseIf, this);
41657         Roo.get(document).on('mousewheel', this.collapseIf, this);
41658         if (!this.editable) {
41659             Roo.get(document).on('keydown', this.listKeyPress, this);
41660         }
41661         
41662         this.fireEvent('expand', this);
41663     },
41664
41665     // private
41666     // Implements the default empty TriggerField.onTriggerClick function
41667     onTriggerClick : function(){
41668         if(this.disabled){
41669             return;
41670         }
41671         if(this.isExpanded()){
41672             this.collapse();
41673             if (!this.blockFocus) {
41674                 this.el.focus();
41675             }
41676             
41677         }else {
41678             this.hasFocus = true;
41679             if(this.triggerAction == 'all') {
41680                 this.doQuery(this.allQuery, true);
41681             } else {
41682                 this.doQuery(this.getRawValue());
41683             }
41684             if (!this.blockFocus) {
41685                 this.el.focus();
41686             }
41687         }
41688     },
41689     listKeyPress : function(e)
41690     {
41691         //Roo.log('listkeypress');
41692         // scroll to first matching element based on key pres..
41693         if (e.isSpecialKey()) {
41694             return false;
41695         }
41696         var k = String.fromCharCode(e.getKey()).toUpperCase();
41697         //Roo.log(k);
41698         var match  = false;
41699         var csel = this.view.getSelectedNodes();
41700         var cselitem = false;
41701         if (csel.length) {
41702             var ix = this.view.indexOf(csel[0]);
41703             cselitem  = this.store.getAt(ix);
41704             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41705                 cselitem = false;
41706             }
41707             
41708         }
41709         
41710         this.store.each(function(v) { 
41711             if (cselitem) {
41712                 // start at existing selection.
41713                 if (cselitem.id == v.id) {
41714                     cselitem = false;
41715                 }
41716                 return;
41717             }
41718                 
41719             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41720                 match = this.store.indexOf(v);
41721                 return false;
41722             }
41723         }, this);
41724         
41725         if (match === false) {
41726             return true; // no more action?
41727         }
41728         // scroll to?
41729         this.view.select(match);
41730         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41731         sn.scrollIntoView(sn.dom.parentNode, false);
41732     }
41733
41734     /** 
41735     * @cfg {Boolean} grow 
41736     * @hide 
41737     */
41738     /** 
41739     * @cfg {Number} growMin 
41740     * @hide 
41741     */
41742     /** 
41743     * @cfg {Number} growMax 
41744     * @hide 
41745     */
41746     /**
41747      * @hide
41748      * @method autoSize
41749      */
41750 });/*
41751  * Copyright(c) 2010-2012, Roo J Solutions Limited
41752  *
41753  * Licence LGPL
41754  *
41755  */
41756
41757 /**
41758  * @class Roo.form.ComboBoxArray
41759  * @extends Roo.form.TextField
41760  * A facebook style adder... for lists of email / people / countries  etc...
41761  * pick multiple items from a combo box, and shows each one.
41762  *
41763  *  Fred [x]  Brian [x]  [Pick another |v]
41764  *
41765  *
41766  *  For this to work: it needs various extra information
41767  *    - normal combo problay has
41768  *      name, hiddenName
41769  *    + displayField, valueField
41770  *
41771  *    For our purpose...
41772  *
41773  *
41774  *   If we change from 'extends' to wrapping...
41775  *   
41776  *  
41777  *
41778  
41779  
41780  * @constructor
41781  * Create a new ComboBoxArray.
41782  * @param {Object} config Configuration options
41783  */
41784  
41785
41786 Roo.form.ComboBoxArray = function(config)
41787 {
41788     this.addEvents({
41789         /**
41790          * @event beforeremove
41791          * Fires before remove the value from the list
41792              * @param {Roo.form.ComboBoxArray} _self This combo box array
41793              * @param {Roo.form.ComboBoxArray.Item} item removed item
41794              */
41795         'beforeremove' : true,
41796         /**
41797          * @event remove
41798          * Fires when remove the value from the list
41799              * @param {Roo.form.ComboBoxArray} _self This combo box array
41800              * @param {Roo.form.ComboBoxArray.Item} item removed item
41801              */
41802         'remove' : true
41803         
41804         
41805     });
41806     
41807     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41808     
41809     this.items = new Roo.util.MixedCollection(false);
41810     
41811     // construct the child combo...
41812     
41813     
41814     
41815     
41816    
41817     
41818 }
41819
41820  
41821 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41822
41823     /**
41824      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41825      */
41826     
41827     lastData : false,
41828     
41829     // behavies liek a hiddne field
41830     inputType:      'hidden',
41831     /**
41832      * @cfg {Number} width The width of the box that displays the selected element
41833      */ 
41834     width:          300,
41835
41836     
41837     
41838     /**
41839      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41840      */
41841     name : false,
41842     /**
41843      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41844      */
41845     hiddenName : false,
41846     
41847     
41848     // private the array of items that are displayed..
41849     items  : false,
41850     // private - the hidden field el.
41851     hiddenEl : false,
41852     // private - the filed el..
41853     el : false,
41854     
41855     //validateValue : function() { return true; }, // all values are ok!
41856     //onAddClick: function() { },
41857     
41858     onRender : function(ct, position) 
41859     {
41860         
41861         // create the standard hidden element
41862         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
41863         
41864         
41865         // give fake names to child combo;
41866         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
41867         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
41868         
41869         this.combo = Roo.factory(this.combo, Roo.form);
41870         this.combo.onRender(ct, position);
41871         if (typeof(this.combo.width) != 'undefined') {
41872             this.combo.onResize(this.combo.width,0);
41873         }
41874         
41875         this.combo.initEvents();
41876         
41877         // assigned so form know we need to do this..
41878         this.store          = this.combo.store;
41879         this.valueField     = this.combo.valueField;
41880         this.displayField   = this.combo.displayField ;
41881         
41882         
41883         this.combo.wrap.addClass('x-cbarray-grp');
41884         
41885         var cbwrap = this.combo.wrap.createChild(
41886             {tag: 'div', cls: 'x-cbarray-cb'},
41887             this.combo.el.dom
41888         );
41889         
41890              
41891         this.hiddenEl = this.combo.wrap.createChild({
41892             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
41893         });
41894         this.el = this.combo.wrap.createChild({
41895             tag: 'input',  type:'hidden' , name: this.name, value : ''
41896         });
41897          //   this.el.dom.removeAttribute("name");
41898         
41899         
41900         this.outerWrap = this.combo.wrap;
41901         this.wrap = cbwrap;
41902         
41903         this.outerWrap.setWidth(this.width);
41904         this.outerWrap.dom.removeChild(this.el.dom);
41905         
41906         this.wrap.dom.appendChild(this.el.dom);
41907         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
41908         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
41909         
41910         this.combo.trigger.setStyle('position','relative');
41911         this.combo.trigger.setStyle('left', '0px');
41912         this.combo.trigger.setStyle('top', '2px');
41913         
41914         this.combo.el.setStyle('vertical-align', 'text-bottom');
41915         
41916         //this.trigger.setStyle('vertical-align', 'top');
41917         
41918         // this should use the code from combo really... on('add' ....)
41919         if (this.adder) {
41920             
41921         
41922             this.adder = this.outerWrap.createChild(
41923                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
41924             var _t = this;
41925             this.adder.on('click', function(e) {
41926                 _t.fireEvent('adderclick', this, e);
41927             }, _t);
41928         }
41929         //var _t = this;
41930         //this.adder.on('click', this.onAddClick, _t);
41931         
41932         
41933         this.combo.on('select', function(cb, rec, ix) {
41934             this.addItem(rec.data);
41935             
41936             cb.setValue('');
41937             cb.el.dom.value = '';
41938             //cb.lastData = rec.data;
41939             // add to list
41940             
41941         }, this);
41942         
41943         
41944     },
41945     
41946     
41947     getName: function()
41948     {
41949         // returns hidden if it's set..
41950         if (!this.rendered) {return ''};
41951         return  this.hiddenName ? this.hiddenName : this.name;
41952         
41953     },
41954     
41955     
41956     onResize: function(w, h){
41957         
41958         return;
41959         // not sure if this is needed..
41960         //this.combo.onResize(w,h);
41961         
41962         if(typeof w != 'number'){
41963             // we do not handle it!?!?
41964             return;
41965         }
41966         var tw = this.combo.trigger.getWidth();
41967         tw += this.addicon ? this.addicon.getWidth() : 0;
41968         tw += this.editicon ? this.editicon.getWidth() : 0;
41969         var x = w - tw;
41970         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
41971             
41972         this.combo.trigger.setStyle('left', '0px');
41973         
41974         if(this.list && this.listWidth === undefined){
41975             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
41976             this.list.setWidth(lw);
41977             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41978         }
41979         
41980     
41981         
41982     },
41983     
41984     addItem: function(rec)
41985     {
41986         var valueField = this.combo.valueField;
41987         var displayField = this.combo.displayField;
41988         if (this.items.indexOfKey(rec[valueField]) > -1) {
41989             //console.log("GOT " + rec.data.id);
41990             return;
41991         }
41992         
41993         var x = new Roo.form.ComboBoxArray.Item({
41994             //id : rec[this.idField],
41995             data : rec,
41996             displayField : displayField ,
41997             tipField : displayField ,
41998             cb : this
41999         });
42000         // use the 
42001         this.items.add(rec[valueField],x);
42002         // add it before the element..
42003         this.updateHiddenEl();
42004         x.render(this.outerWrap, this.wrap.dom);
42005         // add the image handler..
42006     },
42007     
42008     updateHiddenEl : function()
42009     {
42010         this.validate();
42011         if (!this.hiddenEl) {
42012             return;
42013         }
42014         var ar = [];
42015         var idField = this.combo.valueField;
42016         
42017         this.items.each(function(f) {
42018             ar.push(f.data[idField]);
42019            
42020         });
42021         this.hiddenEl.dom.value = ar.join(',');
42022         this.validate();
42023     },
42024     
42025     reset : function()
42026     {
42027         this.items.clear();
42028         
42029         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42030            el.remove();
42031         });
42032         
42033         this.el.dom.value = '';
42034         if (this.hiddenEl) {
42035             this.hiddenEl.dom.value = '';
42036         }
42037         
42038     },
42039     getValue: function()
42040     {
42041         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42042     },
42043     setValue: function(v) // not a valid action - must use addItems..
42044     {
42045          
42046         this.reset();
42047         
42048         
42049         
42050         if (this.store.isLocal && (typeof(v) == 'string')) {
42051             // then we can use the store to find the values..
42052             // comma seperated at present.. this needs to allow JSON based encoding..
42053             this.hiddenEl.value  = v;
42054             var v_ar = [];
42055             Roo.each(v.split(','), function(k) {
42056                 Roo.log("CHECK " + this.valueField + ',' + k);
42057                 var li = this.store.query(this.valueField, k);
42058                 if (!li.length) {
42059                     return;
42060                 }
42061                 var add = {};
42062                 add[this.valueField] = k;
42063                 add[this.displayField] = li.item(0).data[this.displayField];
42064                 
42065                 this.addItem(add);
42066             }, this) 
42067              
42068         }
42069         if (typeof(v) == 'object' ) {
42070             // then let's assume it's an array of objects..
42071             Roo.each(v, function(l) {
42072                 this.addItem(l);
42073             }, this);
42074              
42075         }
42076         
42077         
42078     },
42079     setFromData: function(v)
42080     {
42081         // this recieves an object, if setValues is called.
42082         this.reset();
42083         this.el.dom.value = v[this.displayField];
42084         this.hiddenEl.dom.value = v[this.valueField];
42085         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42086             return;
42087         }
42088         var kv = v[this.valueField];
42089         var dv = v[this.displayField];
42090         kv = typeof(kv) != 'string' ? '' : kv;
42091         dv = typeof(dv) != 'string' ? '' : dv;
42092         
42093         
42094         var keys = kv.split(',');
42095         var display = dv.split(',');
42096         for (var i = 0 ; i < keys.length; i++) {
42097             
42098             add = {};
42099             add[this.valueField] = keys[i];
42100             add[this.displayField] = display[i];
42101             this.addItem(add);
42102         }
42103       
42104         
42105     },
42106     
42107     /**
42108      * Validates the combox array value
42109      * @return {Boolean} True if the value is valid, else false
42110      */
42111     validate : function(){
42112         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42113             this.clearInvalid();
42114             return true;
42115         }
42116         return false;
42117     },
42118     
42119     validateValue : function(value){
42120         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42121         
42122     },
42123     
42124     /*@
42125      * overide
42126      * 
42127      */
42128     isDirty : function() {
42129         if(this.disabled) {
42130             return false;
42131         }
42132         
42133         try {
42134             var d = Roo.decode(String(this.originalValue));
42135         } catch (e) {
42136             return String(this.getValue()) !== String(this.originalValue);
42137         }
42138         
42139         var originalValue = [];
42140         
42141         for (var i = 0; i < d.length; i++){
42142             originalValue.push(d[i][this.valueField]);
42143         }
42144         
42145         return String(this.getValue()) !== String(originalValue.join(','));
42146         
42147     }
42148     
42149 });
42150
42151
42152
42153 /**
42154  * @class Roo.form.ComboBoxArray.Item
42155  * @extends Roo.BoxComponent
42156  * A selected item in the list
42157  *  Fred [x]  Brian [x]  [Pick another |v]
42158  * 
42159  * @constructor
42160  * Create a new item.
42161  * @param {Object} config Configuration options
42162  */
42163  
42164 Roo.form.ComboBoxArray.Item = function(config) {
42165     config.id = Roo.id();
42166     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42167 }
42168
42169 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42170     data : {},
42171     cb: false,
42172     displayField : false,
42173     tipField : false,
42174     
42175     
42176     defaultAutoCreate : {
42177         tag: 'div',
42178         cls: 'x-cbarray-item',
42179         cn : [ 
42180             { tag: 'div' },
42181             {
42182                 tag: 'img',
42183                 width:16,
42184                 height : 16,
42185                 src : Roo.BLANK_IMAGE_URL ,
42186                 align: 'center'
42187             }
42188         ]
42189         
42190     },
42191     
42192  
42193     onRender : function(ct, position)
42194     {
42195         Roo.form.Field.superclass.onRender.call(this, ct, position);
42196         
42197         if(!this.el){
42198             var cfg = this.getAutoCreate();
42199             this.el = ct.createChild(cfg, position);
42200         }
42201         
42202         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42203         
42204         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42205             this.cb.renderer(this.data) :
42206             String.format('{0}',this.data[this.displayField]);
42207         
42208             
42209         this.el.child('div').dom.setAttribute('qtip',
42210                         String.format('{0}',this.data[this.tipField])
42211         );
42212         
42213         this.el.child('img').on('click', this.remove, this);
42214         
42215     },
42216    
42217     remove : function()
42218     {
42219         if(this.cb.disabled){
42220             return;
42221         }
42222         
42223         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42224             this.cb.items.remove(this);
42225             this.el.child('img').un('click', this.remove, this);
42226             this.el.remove();
42227             this.cb.updateHiddenEl();
42228
42229             this.cb.fireEvent('remove', this.cb, this);
42230         }
42231         
42232     }
42233 });/*
42234  * Based on:
42235  * Ext JS Library 1.1.1
42236  * Copyright(c) 2006-2007, Ext JS, LLC.
42237  *
42238  * Originally Released Under LGPL - original licence link has changed is not relivant.
42239  *
42240  * Fork - LGPL
42241  * <script type="text/javascript">
42242  */
42243 /**
42244  * @class Roo.form.Checkbox
42245  * @extends Roo.form.Field
42246  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42247  * @constructor
42248  * Creates a new Checkbox
42249  * @param {Object} config Configuration options
42250  */
42251 Roo.form.Checkbox = function(config){
42252     Roo.form.Checkbox.superclass.constructor.call(this, config);
42253     this.addEvents({
42254         /**
42255          * @event check
42256          * Fires when the checkbox is checked or unchecked.
42257              * @param {Roo.form.Checkbox} this This checkbox
42258              * @param {Boolean} checked The new checked value
42259              */
42260         check : true
42261     });
42262 };
42263
42264 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42265     /**
42266      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42267      */
42268     focusClass : undefined,
42269     /**
42270      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42271      */
42272     fieldClass: "x-form-field",
42273     /**
42274      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42275      */
42276     checked: false,
42277     /**
42278      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42279      * {tag: "input", type: "checkbox", autocomplete: "off"})
42280      */
42281     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42282     /**
42283      * @cfg {String} boxLabel The text that appears beside the checkbox
42284      */
42285     boxLabel : "",
42286     /**
42287      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42288      */  
42289     inputValue : '1',
42290     /**
42291      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42292      */
42293      valueOff: '0', // value when not checked..
42294
42295     actionMode : 'viewEl', 
42296     //
42297     // private
42298     itemCls : 'x-menu-check-item x-form-item',
42299     groupClass : 'x-menu-group-item',
42300     inputType : 'hidden',
42301     
42302     
42303     inSetChecked: false, // check that we are not calling self...
42304     
42305     inputElement: false, // real input element?
42306     basedOn: false, // ????
42307     
42308     isFormField: true, // not sure where this is needed!!!!
42309
42310     onResize : function(){
42311         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42312         if(!this.boxLabel){
42313             this.el.alignTo(this.wrap, 'c-c');
42314         }
42315     },
42316
42317     initEvents : function(){
42318         Roo.form.Checkbox.superclass.initEvents.call(this);
42319         this.el.on("click", this.onClick,  this);
42320         this.el.on("change", this.onClick,  this);
42321     },
42322
42323
42324     getResizeEl : function(){
42325         return this.wrap;
42326     },
42327
42328     getPositionEl : function(){
42329         return this.wrap;
42330     },
42331
42332     // private
42333     onRender : function(ct, position){
42334         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42335         /*
42336         if(this.inputValue !== undefined){
42337             this.el.dom.value = this.inputValue;
42338         }
42339         */
42340         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42341         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42342         var viewEl = this.wrap.createChild({ 
42343             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42344         this.viewEl = viewEl;   
42345         this.wrap.on('click', this.onClick,  this); 
42346         
42347         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42348         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42349         
42350         
42351         
42352         if(this.boxLabel){
42353             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42354         //    viewEl.on('click', this.onClick,  this); 
42355         }
42356         //if(this.checked){
42357             this.setChecked(this.checked);
42358         //}else{
42359             //this.checked = this.el.dom;
42360         //}
42361
42362     },
42363
42364     // private
42365     initValue : Roo.emptyFn,
42366
42367     /**
42368      * Returns the checked state of the checkbox.
42369      * @return {Boolean} True if checked, else false
42370      */
42371     getValue : function(){
42372         if(this.el){
42373             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42374         }
42375         return this.valueOff;
42376         
42377     },
42378
42379         // private
42380     onClick : function(){ 
42381         if (this.disabled) {
42382             return;
42383         }
42384         this.setChecked(!this.checked);
42385
42386         //if(this.el.dom.checked != this.checked){
42387         //    this.setValue(this.el.dom.checked);
42388        // }
42389     },
42390
42391     /**
42392      * Sets the checked state of the checkbox.
42393      * On is always based on a string comparison between inputValue and the param.
42394      * @param {Boolean/String} value - the value to set 
42395      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42396      */
42397     setValue : function(v,suppressEvent){
42398         
42399         
42400         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42401         //if(this.el && this.el.dom){
42402         //    this.el.dom.checked = this.checked;
42403         //    this.el.dom.defaultChecked = this.checked;
42404         //}
42405         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42406         //this.fireEvent("check", this, this.checked);
42407     },
42408     // private..
42409     setChecked : function(state,suppressEvent)
42410     {
42411         if (this.inSetChecked) {
42412             this.checked = state;
42413             return;
42414         }
42415         
42416     
42417         if(this.wrap){
42418             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42419         }
42420         this.checked = state;
42421         if(suppressEvent !== true){
42422             this.fireEvent('check', this, state);
42423         }
42424         this.inSetChecked = true;
42425         this.el.dom.value = state ? this.inputValue : this.valueOff;
42426         this.inSetChecked = false;
42427         
42428     },
42429     // handle setting of hidden value by some other method!!?!?
42430     setFromHidden: function()
42431     {
42432         if(!this.el){
42433             return;
42434         }
42435         //console.log("SET FROM HIDDEN");
42436         //alert('setFrom hidden');
42437         this.setValue(this.el.dom.value);
42438     },
42439     
42440     onDestroy : function()
42441     {
42442         if(this.viewEl){
42443             Roo.get(this.viewEl).remove();
42444         }
42445          
42446         Roo.form.Checkbox.superclass.onDestroy.call(this);
42447     },
42448     
42449     setBoxLabel : function(str)
42450     {
42451         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42452     }
42453
42454 });/*
42455  * Based on:
42456  * Ext JS Library 1.1.1
42457  * Copyright(c) 2006-2007, Ext JS, LLC.
42458  *
42459  * Originally Released Under LGPL - original licence link has changed is not relivant.
42460  *
42461  * Fork - LGPL
42462  * <script type="text/javascript">
42463  */
42464  
42465 /**
42466  * @class Roo.form.Radio
42467  * @extends Roo.form.Checkbox
42468  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42469  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42470  * @constructor
42471  * Creates a new Radio
42472  * @param {Object} config Configuration options
42473  */
42474 Roo.form.Radio = function(){
42475     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42476 };
42477 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42478     inputType: 'radio',
42479
42480     /**
42481      * If this radio is part of a group, it will return the selected value
42482      * @return {String}
42483      */
42484     getGroupValue : function(){
42485         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42486     },
42487     
42488     
42489     onRender : function(ct, position){
42490         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42491         
42492         if(this.inputValue !== undefined){
42493             this.el.dom.value = this.inputValue;
42494         }
42495          
42496         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42497         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42498         //var viewEl = this.wrap.createChild({ 
42499         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42500         //this.viewEl = viewEl;   
42501         //this.wrap.on('click', this.onClick,  this); 
42502         
42503         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42504         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42505         
42506         
42507         
42508         if(this.boxLabel){
42509             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42510         //    viewEl.on('click', this.onClick,  this); 
42511         }
42512          if(this.checked){
42513             this.el.dom.checked =   'checked' ;
42514         }
42515          
42516     } 
42517     
42518     
42519 });//<script type="text/javascript">
42520
42521 /*
42522  * Based  Ext JS Library 1.1.1
42523  * Copyright(c) 2006-2007, Ext JS, LLC.
42524  * LGPL
42525  *
42526  */
42527  
42528 /**
42529  * @class Roo.HtmlEditorCore
42530  * @extends Roo.Component
42531  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42532  *
42533  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42534  */
42535
42536 Roo.HtmlEditorCore = function(config){
42537     
42538     
42539     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42540     
42541     
42542     this.addEvents({
42543         /**
42544          * @event initialize
42545          * Fires when the editor is fully initialized (including the iframe)
42546          * @param {Roo.HtmlEditorCore} this
42547          */
42548         initialize: true,
42549         /**
42550          * @event activate
42551          * Fires when the editor is first receives the focus. Any insertion must wait
42552          * until after this event.
42553          * @param {Roo.HtmlEditorCore} this
42554          */
42555         activate: true,
42556          /**
42557          * @event beforesync
42558          * Fires before the textarea is updated with content from the editor iframe. Return false
42559          * to cancel the sync.
42560          * @param {Roo.HtmlEditorCore} this
42561          * @param {String} html
42562          */
42563         beforesync: true,
42564          /**
42565          * @event beforepush
42566          * Fires before the iframe editor is updated with content from the textarea. Return false
42567          * to cancel the push.
42568          * @param {Roo.HtmlEditorCore} this
42569          * @param {String} html
42570          */
42571         beforepush: true,
42572          /**
42573          * @event sync
42574          * Fires when the textarea is updated with content from the editor iframe.
42575          * @param {Roo.HtmlEditorCore} this
42576          * @param {String} html
42577          */
42578         sync: true,
42579          /**
42580          * @event push
42581          * Fires when the iframe editor is updated with content from the textarea.
42582          * @param {Roo.HtmlEditorCore} this
42583          * @param {String} html
42584          */
42585         push: true,
42586         
42587         /**
42588          * @event editorevent
42589          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42590          * @param {Roo.HtmlEditorCore} this
42591          */
42592         editorevent: true
42593         
42594     });
42595     
42596     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42597     
42598     // defaults : white / black...
42599     this.applyBlacklists();
42600     
42601     
42602     
42603 };
42604
42605
42606 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42607
42608
42609      /**
42610      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42611      */
42612     
42613     owner : false,
42614     
42615      /**
42616      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42617      *                        Roo.resizable.
42618      */
42619     resizable : false,
42620      /**
42621      * @cfg {Number} height (in pixels)
42622      */   
42623     height: 300,
42624    /**
42625      * @cfg {Number} width (in pixels)
42626      */   
42627     width: 500,
42628     
42629     /**
42630      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42631      * 
42632      */
42633     stylesheets: false,
42634     
42635     // id of frame..
42636     frameId: false,
42637     
42638     // private properties
42639     validationEvent : false,
42640     deferHeight: true,
42641     initialized : false,
42642     activated : false,
42643     sourceEditMode : false,
42644     onFocus : Roo.emptyFn,
42645     iframePad:3,
42646     hideMode:'offsets',
42647     
42648     clearUp: true,
42649     
42650     // blacklist + whitelisted elements..
42651     black: false,
42652     white: false,
42653      
42654     
42655
42656     /**
42657      * Protected method that will not generally be called directly. It
42658      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42659      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42660      */
42661     getDocMarkup : function(){
42662         // body styles..
42663         var st = '';
42664         
42665         // inherit styels from page...?? 
42666         if (this.stylesheets === false) {
42667             
42668             Roo.get(document.head).select('style').each(function(node) {
42669                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42670             });
42671             
42672             Roo.get(document.head).select('link').each(function(node) { 
42673                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42674             });
42675             
42676         } else if (!this.stylesheets.length) {
42677                 // simple..
42678                 st = '<style type="text/css">' +
42679                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42680                    '</style>';
42681         } else { 
42682             
42683         }
42684         
42685         st +=  '<style type="text/css">' +
42686             'IMG { cursor: pointer } ' +
42687         '</style>';
42688
42689         
42690         return '<html><head>' + st  +
42691             //<style type="text/css">' +
42692             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42693             //'</style>' +
42694             ' </head><body class="roo-htmleditor-body"></body></html>';
42695     },
42696
42697     // private
42698     onRender : function(ct, position)
42699     {
42700         var _t = this;
42701         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42702         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42703         
42704         
42705         this.el.dom.style.border = '0 none';
42706         this.el.dom.setAttribute('tabIndex', -1);
42707         this.el.addClass('x-hidden hide');
42708         
42709         
42710         
42711         if(Roo.isIE){ // fix IE 1px bogus margin
42712             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42713         }
42714        
42715         
42716         this.frameId = Roo.id();
42717         
42718          
42719         
42720         var iframe = this.owner.wrap.createChild({
42721             tag: 'iframe',
42722             cls: 'form-control', // bootstrap..
42723             id: this.frameId,
42724             name: this.frameId,
42725             frameBorder : 'no',
42726             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42727         }, this.el
42728         );
42729         
42730         
42731         this.iframe = iframe.dom;
42732
42733          this.assignDocWin();
42734         
42735         this.doc.designMode = 'on';
42736        
42737         this.doc.open();
42738         this.doc.write(this.getDocMarkup());
42739         this.doc.close();
42740
42741         
42742         var task = { // must defer to wait for browser to be ready
42743             run : function(){
42744                 //console.log("run task?" + this.doc.readyState);
42745                 this.assignDocWin();
42746                 if(this.doc.body || this.doc.readyState == 'complete'){
42747                     try {
42748                         this.doc.designMode="on";
42749                     } catch (e) {
42750                         return;
42751                     }
42752                     Roo.TaskMgr.stop(task);
42753                     this.initEditor.defer(10, this);
42754                 }
42755             },
42756             interval : 10,
42757             duration: 10000,
42758             scope: this
42759         };
42760         Roo.TaskMgr.start(task);
42761
42762     },
42763
42764     // private
42765     onResize : function(w, h)
42766     {
42767          Roo.log('resize: ' +w + ',' + h );
42768         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42769         if(!this.iframe){
42770             return;
42771         }
42772         if(typeof w == 'number'){
42773             
42774             this.iframe.style.width = w + 'px';
42775         }
42776         if(typeof h == 'number'){
42777             
42778             this.iframe.style.height = h + 'px';
42779             if(this.doc){
42780                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42781             }
42782         }
42783         
42784     },
42785
42786     /**
42787      * Toggles the editor between standard and source edit mode.
42788      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42789      */
42790     toggleSourceEdit : function(sourceEditMode){
42791         
42792         this.sourceEditMode = sourceEditMode === true;
42793         
42794         if(this.sourceEditMode){
42795  
42796             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42797             
42798         }else{
42799             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42800             //this.iframe.className = '';
42801             this.deferFocus();
42802         }
42803         //this.setSize(this.owner.wrap.getSize());
42804         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42805     },
42806
42807     
42808   
42809
42810     /**
42811      * Protected method that will not generally be called directly. If you need/want
42812      * custom HTML cleanup, this is the method you should override.
42813      * @param {String} html The HTML to be cleaned
42814      * return {String} The cleaned HTML
42815      */
42816     cleanHtml : function(html){
42817         html = String(html);
42818         if(html.length > 5){
42819             if(Roo.isSafari){ // strip safari nonsense
42820                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42821             }
42822         }
42823         if(html == '&nbsp;'){
42824             html = '';
42825         }
42826         return html;
42827     },
42828
42829     /**
42830      * HTML Editor -> Textarea
42831      * Protected method that will not generally be called directly. Syncs the contents
42832      * of the editor iframe with the textarea.
42833      */
42834     syncValue : function(){
42835         if(this.initialized){
42836             var bd = (this.doc.body || this.doc.documentElement);
42837             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42838             var html = bd.innerHTML;
42839             if(Roo.isSafari){
42840                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42841                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42842                 if(m && m[1]){
42843                     html = '<div style="'+m[0]+'">' + html + '</div>';
42844                 }
42845             }
42846             html = this.cleanHtml(html);
42847             // fix up the special chars.. normaly like back quotes in word...
42848             // however we do not want to do this with chinese..
42849             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42850                 var cc = b.charCodeAt();
42851                 if (
42852                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42853                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42854                     (cc >= 0xf900 && cc < 0xfb00 )
42855                 ) {
42856                         return b;
42857                 }
42858                 return "&#"+cc+";" 
42859             });
42860             if(this.owner.fireEvent('beforesync', this, html) !== false){
42861                 this.el.dom.value = html;
42862                 this.owner.fireEvent('sync', this, html);
42863             }
42864         }
42865     },
42866
42867     /**
42868      * Protected method that will not generally be called directly. Pushes the value of the textarea
42869      * into the iframe editor.
42870      */
42871     pushValue : function(){
42872         if(this.initialized){
42873             var v = this.el.dom.value.trim();
42874             
42875 //            if(v.length < 1){
42876 //                v = '&#160;';
42877 //            }
42878             
42879             if(this.owner.fireEvent('beforepush', this, v) !== false){
42880                 var d = (this.doc.body || this.doc.documentElement);
42881                 d.innerHTML = v;
42882                 this.cleanUpPaste();
42883                 this.el.dom.value = d.innerHTML;
42884                 this.owner.fireEvent('push', this, v);
42885             }
42886         }
42887     },
42888
42889     // private
42890     deferFocus : function(){
42891         this.focus.defer(10, this);
42892     },
42893
42894     // doc'ed in Field
42895     focus : function(){
42896         if(this.win && !this.sourceEditMode){
42897             this.win.focus();
42898         }else{
42899             this.el.focus();
42900         }
42901     },
42902     
42903     assignDocWin: function()
42904     {
42905         var iframe = this.iframe;
42906         
42907          if(Roo.isIE){
42908             this.doc = iframe.contentWindow.document;
42909             this.win = iframe.contentWindow;
42910         } else {
42911 //            if (!Roo.get(this.frameId)) {
42912 //                return;
42913 //            }
42914 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
42915 //            this.win = Roo.get(this.frameId).dom.contentWindow;
42916             
42917             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
42918                 return;
42919             }
42920             
42921             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
42922             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
42923         }
42924     },
42925     
42926     // private
42927     initEditor : function(){
42928         //console.log("INIT EDITOR");
42929         this.assignDocWin();
42930         
42931         
42932         
42933         this.doc.designMode="on";
42934         this.doc.open();
42935         this.doc.write(this.getDocMarkup());
42936         this.doc.close();
42937         
42938         var dbody = (this.doc.body || this.doc.documentElement);
42939         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
42940         // this copies styles from the containing element into thsi one..
42941         // not sure why we need all of this..
42942         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
42943         
42944         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
42945         //ss['background-attachment'] = 'fixed'; // w3c
42946         dbody.bgProperties = 'fixed'; // ie
42947         //Roo.DomHelper.applyStyles(dbody, ss);
42948         Roo.EventManager.on(this.doc, {
42949             //'mousedown': this.onEditorEvent,
42950             'mouseup': this.onEditorEvent,
42951             'dblclick': this.onEditorEvent,
42952             'click': this.onEditorEvent,
42953             'keyup': this.onEditorEvent,
42954             buffer:100,
42955             scope: this
42956         });
42957         if(Roo.isGecko){
42958             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
42959         }
42960         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
42961             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
42962         }
42963         this.initialized = true;
42964
42965         this.owner.fireEvent('initialize', this);
42966         this.pushValue();
42967     },
42968
42969     // private
42970     onDestroy : function(){
42971         
42972         
42973         
42974         if(this.rendered){
42975             
42976             //for (var i =0; i < this.toolbars.length;i++) {
42977             //    // fixme - ask toolbars for heights?
42978             //    this.toolbars[i].onDestroy();
42979            // }
42980             
42981             //this.wrap.dom.innerHTML = '';
42982             //this.wrap.remove();
42983         }
42984     },
42985
42986     // private
42987     onFirstFocus : function(){
42988         
42989         this.assignDocWin();
42990         
42991         
42992         this.activated = true;
42993          
42994     
42995         if(Roo.isGecko){ // prevent silly gecko errors
42996             this.win.focus();
42997             var s = this.win.getSelection();
42998             if(!s.focusNode || s.focusNode.nodeType != 3){
42999                 var r = s.getRangeAt(0);
43000                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43001                 r.collapse(true);
43002                 this.deferFocus();
43003             }
43004             try{
43005                 this.execCmd('useCSS', true);
43006                 this.execCmd('styleWithCSS', false);
43007             }catch(e){}
43008         }
43009         this.owner.fireEvent('activate', this);
43010     },
43011
43012     // private
43013     adjustFont: function(btn){
43014         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43015         //if(Roo.isSafari){ // safari
43016         //    adjust *= 2;
43017        // }
43018         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43019         if(Roo.isSafari){ // safari
43020             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43021             v =  (v < 10) ? 10 : v;
43022             v =  (v > 48) ? 48 : v;
43023             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43024             
43025         }
43026         
43027         
43028         v = Math.max(1, v+adjust);
43029         
43030         this.execCmd('FontSize', v  );
43031     },
43032
43033     onEditorEvent : function(e)
43034     {
43035         this.owner.fireEvent('editorevent', this, e);
43036       //  this.updateToolbar();
43037         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43038     },
43039
43040     insertTag : function(tg)
43041     {
43042         // could be a bit smarter... -> wrap the current selected tRoo..
43043         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43044             
43045             range = this.createRange(this.getSelection());
43046             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43047             wrappingNode.appendChild(range.extractContents());
43048             range.insertNode(wrappingNode);
43049
43050             return;
43051             
43052             
43053             
43054         }
43055         this.execCmd("formatblock",   tg);
43056         
43057     },
43058     
43059     insertText : function(txt)
43060     {
43061         
43062         
43063         var range = this.createRange();
43064         range.deleteContents();
43065                //alert(Sender.getAttribute('label'));
43066                
43067         range.insertNode(this.doc.createTextNode(txt));
43068     } ,
43069     
43070      
43071
43072     /**
43073      * Executes a Midas editor command on the editor document and performs necessary focus and
43074      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43075      * @param {String} cmd The Midas command
43076      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43077      */
43078     relayCmd : function(cmd, value){
43079         this.win.focus();
43080         this.execCmd(cmd, value);
43081         this.owner.fireEvent('editorevent', this);
43082         //this.updateToolbar();
43083         this.owner.deferFocus();
43084     },
43085
43086     /**
43087      * Executes a Midas editor command directly on the editor document.
43088      * For visual commands, you should use {@link #relayCmd} instead.
43089      * <b>This should only be called after the editor is initialized.</b>
43090      * @param {String} cmd The Midas command
43091      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43092      */
43093     execCmd : function(cmd, value){
43094         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43095         this.syncValue();
43096     },
43097  
43098  
43099    
43100     /**
43101      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43102      * to insert tRoo.
43103      * @param {String} text | dom node.. 
43104      */
43105     insertAtCursor : function(text)
43106     {
43107         
43108         
43109         
43110         if(!this.activated){
43111             return;
43112         }
43113         /*
43114         if(Roo.isIE){
43115             this.win.focus();
43116             var r = this.doc.selection.createRange();
43117             if(r){
43118                 r.collapse(true);
43119                 r.pasteHTML(text);
43120                 this.syncValue();
43121                 this.deferFocus();
43122             
43123             }
43124             return;
43125         }
43126         */
43127         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43128             this.win.focus();
43129             
43130             
43131             // from jquery ui (MIT licenced)
43132             var range, node;
43133             var win = this.win;
43134             
43135             if (win.getSelection && win.getSelection().getRangeAt) {
43136                 range = win.getSelection().getRangeAt(0);
43137                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43138                 range.insertNode(node);
43139             } else if (win.document.selection && win.document.selection.createRange) {
43140                 // no firefox support
43141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43142                 win.document.selection.createRange().pasteHTML(txt);
43143             } else {
43144                 // no firefox support
43145                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43146                 this.execCmd('InsertHTML', txt);
43147             } 
43148             
43149             this.syncValue();
43150             
43151             this.deferFocus();
43152         }
43153     },
43154  // private
43155     mozKeyPress : function(e){
43156         if(e.ctrlKey){
43157             var c = e.getCharCode(), cmd;
43158           
43159             if(c > 0){
43160                 c = String.fromCharCode(c).toLowerCase();
43161                 switch(c){
43162                     case 'b':
43163                         cmd = 'bold';
43164                         break;
43165                     case 'i':
43166                         cmd = 'italic';
43167                         break;
43168                     
43169                     case 'u':
43170                         cmd = 'underline';
43171                         break;
43172                     
43173                     case 'v':
43174                         this.cleanUpPaste.defer(100, this);
43175                         return;
43176                         
43177                 }
43178                 if(cmd){
43179                     this.win.focus();
43180                     this.execCmd(cmd);
43181                     this.deferFocus();
43182                     e.preventDefault();
43183                 }
43184                 
43185             }
43186         }
43187     },
43188
43189     // private
43190     fixKeys : function(){ // load time branching for fastest keydown performance
43191         if(Roo.isIE){
43192             return function(e){
43193                 var k = e.getKey(), r;
43194                 if(k == e.TAB){
43195                     e.stopEvent();
43196                     r = this.doc.selection.createRange();
43197                     if(r){
43198                         r.collapse(true);
43199                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43200                         this.deferFocus();
43201                     }
43202                     return;
43203                 }
43204                 
43205                 if(k == e.ENTER){
43206                     r = this.doc.selection.createRange();
43207                     if(r){
43208                         var target = r.parentElement();
43209                         if(!target || target.tagName.toLowerCase() != 'li'){
43210                             e.stopEvent();
43211                             r.pasteHTML('<br />');
43212                             r.collapse(false);
43213                             r.select();
43214                         }
43215                     }
43216                 }
43217                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43218                     this.cleanUpPaste.defer(100, this);
43219                     return;
43220                 }
43221                 
43222                 
43223             };
43224         }else if(Roo.isOpera){
43225             return function(e){
43226                 var k = e.getKey();
43227                 if(k == e.TAB){
43228                     e.stopEvent();
43229                     this.win.focus();
43230                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43231                     this.deferFocus();
43232                 }
43233                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43234                     this.cleanUpPaste.defer(100, this);
43235                     return;
43236                 }
43237                 
43238             };
43239         }else if(Roo.isSafari){
43240             return function(e){
43241                 var k = e.getKey();
43242                 
43243                 if(k == e.TAB){
43244                     e.stopEvent();
43245                     this.execCmd('InsertText','\t');
43246                     this.deferFocus();
43247                     return;
43248                 }
43249                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43250                     this.cleanUpPaste.defer(100, this);
43251                     return;
43252                 }
43253                 
43254              };
43255         }
43256     }(),
43257     
43258     getAllAncestors: function()
43259     {
43260         var p = this.getSelectedNode();
43261         var a = [];
43262         if (!p) {
43263             a.push(p); // push blank onto stack..
43264             p = this.getParentElement();
43265         }
43266         
43267         
43268         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43269             a.push(p);
43270             p = p.parentNode;
43271         }
43272         a.push(this.doc.body);
43273         return a;
43274     },
43275     lastSel : false,
43276     lastSelNode : false,
43277     
43278     
43279     getSelection : function() 
43280     {
43281         this.assignDocWin();
43282         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43283     },
43284     
43285     getSelectedNode: function() 
43286     {
43287         // this may only work on Gecko!!!
43288         
43289         // should we cache this!!!!
43290         
43291         
43292         
43293          
43294         var range = this.createRange(this.getSelection()).cloneRange();
43295         
43296         if (Roo.isIE) {
43297             var parent = range.parentElement();
43298             while (true) {
43299                 var testRange = range.duplicate();
43300                 testRange.moveToElementText(parent);
43301                 if (testRange.inRange(range)) {
43302                     break;
43303                 }
43304                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43305                     break;
43306                 }
43307                 parent = parent.parentElement;
43308             }
43309             return parent;
43310         }
43311         
43312         // is ancestor a text element.
43313         var ac =  range.commonAncestorContainer;
43314         if (ac.nodeType == 3) {
43315             ac = ac.parentNode;
43316         }
43317         
43318         var ar = ac.childNodes;
43319          
43320         var nodes = [];
43321         var other_nodes = [];
43322         var has_other_nodes = false;
43323         for (var i=0;i<ar.length;i++) {
43324             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43325                 continue;
43326             }
43327             // fullly contained node.
43328             
43329             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43330                 nodes.push(ar[i]);
43331                 continue;
43332             }
43333             
43334             // probably selected..
43335             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43336                 other_nodes.push(ar[i]);
43337                 continue;
43338             }
43339             // outer..
43340             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43341                 continue;
43342             }
43343             
43344             
43345             has_other_nodes = true;
43346         }
43347         if (!nodes.length && other_nodes.length) {
43348             nodes= other_nodes;
43349         }
43350         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43351             return false;
43352         }
43353         
43354         return nodes[0];
43355     },
43356     createRange: function(sel)
43357     {
43358         // this has strange effects when using with 
43359         // top toolbar - not sure if it's a great idea.
43360         //this.editor.contentWindow.focus();
43361         if (typeof sel != "undefined") {
43362             try {
43363                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43364             } catch(e) {
43365                 return this.doc.createRange();
43366             }
43367         } else {
43368             return this.doc.createRange();
43369         }
43370     },
43371     getParentElement: function()
43372     {
43373         
43374         this.assignDocWin();
43375         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43376         
43377         var range = this.createRange(sel);
43378          
43379         try {
43380             var p = range.commonAncestorContainer;
43381             while (p.nodeType == 3) { // text node
43382                 p = p.parentNode;
43383             }
43384             return p;
43385         } catch (e) {
43386             return null;
43387         }
43388     
43389     },
43390     /***
43391      *
43392      * Range intersection.. the hard stuff...
43393      *  '-1' = before
43394      *  '0' = hits..
43395      *  '1' = after.
43396      *         [ -- selected range --- ]
43397      *   [fail]                        [fail]
43398      *
43399      *    basically..
43400      *      if end is before start or  hits it. fail.
43401      *      if start is after end or hits it fail.
43402      *
43403      *   if either hits (but other is outside. - then it's not 
43404      *   
43405      *    
43406      **/
43407     
43408     
43409     // @see http://www.thismuchiknow.co.uk/?p=64.
43410     rangeIntersectsNode : function(range, node)
43411     {
43412         var nodeRange = node.ownerDocument.createRange();
43413         try {
43414             nodeRange.selectNode(node);
43415         } catch (e) {
43416             nodeRange.selectNodeContents(node);
43417         }
43418     
43419         var rangeStartRange = range.cloneRange();
43420         rangeStartRange.collapse(true);
43421     
43422         var rangeEndRange = range.cloneRange();
43423         rangeEndRange.collapse(false);
43424     
43425         var nodeStartRange = nodeRange.cloneRange();
43426         nodeStartRange.collapse(true);
43427     
43428         var nodeEndRange = nodeRange.cloneRange();
43429         nodeEndRange.collapse(false);
43430     
43431         return rangeStartRange.compareBoundaryPoints(
43432                  Range.START_TO_START, nodeEndRange) == -1 &&
43433                rangeEndRange.compareBoundaryPoints(
43434                  Range.START_TO_START, nodeStartRange) == 1;
43435         
43436          
43437     },
43438     rangeCompareNode : function(range, node)
43439     {
43440         var nodeRange = node.ownerDocument.createRange();
43441         try {
43442             nodeRange.selectNode(node);
43443         } catch (e) {
43444             nodeRange.selectNodeContents(node);
43445         }
43446         
43447         
43448         range.collapse(true);
43449     
43450         nodeRange.collapse(true);
43451      
43452         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43453         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43454          
43455         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43456         
43457         var nodeIsBefore   =  ss == 1;
43458         var nodeIsAfter    = ee == -1;
43459         
43460         if (nodeIsBefore && nodeIsAfter) {
43461             return 0; // outer
43462         }
43463         if (!nodeIsBefore && nodeIsAfter) {
43464             return 1; //right trailed.
43465         }
43466         
43467         if (nodeIsBefore && !nodeIsAfter) {
43468             return 2;  // left trailed.
43469         }
43470         // fully contined.
43471         return 3;
43472     },
43473
43474     // private? - in a new class?
43475     cleanUpPaste :  function()
43476     {
43477         // cleans up the whole document..
43478         Roo.log('cleanuppaste');
43479         
43480         this.cleanUpChildren(this.doc.body);
43481         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43482         if (clean != this.doc.body.innerHTML) {
43483             this.doc.body.innerHTML = clean;
43484         }
43485         
43486     },
43487     
43488     cleanWordChars : function(input) {// change the chars to hex code
43489         var he = Roo.HtmlEditorCore;
43490         
43491         var output = input;
43492         Roo.each(he.swapCodes, function(sw) { 
43493             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43494             
43495             output = output.replace(swapper, sw[1]);
43496         });
43497         
43498         return output;
43499     },
43500     
43501     
43502     cleanUpChildren : function (n)
43503     {
43504         if (!n.childNodes.length) {
43505             return;
43506         }
43507         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43508            this.cleanUpChild(n.childNodes[i]);
43509         }
43510     },
43511     
43512     
43513         
43514     
43515     cleanUpChild : function (node)
43516     {
43517         var ed = this;
43518         //console.log(node);
43519         if (node.nodeName == "#text") {
43520             // clean up silly Windows -- stuff?
43521             return; 
43522         }
43523         if (node.nodeName == "#comment") {
43524             node.parentNode.removeChild(node);
43525             // clean up silly Windows -- stuff?
43526             return; 
43527         }
43528         var lcname = node.tagName.toLowerCase();
43529         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43530         // whitelist of tags..
43531         
43532         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43533             // remove node.
43534             node.parentNode.removeChild(node);
43535             return;
43536             
43537         }
43538         
43539         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43540         
43541         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43542         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43543         
43544         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43545         //    remove_keep_children = true;
43546         //}
43547         
43548         if (remove_keep_children) {
43549             this.cleanUpChildren(node);
43550             // inserts everything just before this node...
43551             while (node.childNodes.length) {
43552                 var cn = node.childNodes[0];
43553                 node.removeChild(cn);
43554                 node.parentNode.insertBefore(cn, node);
43555             }
43556             node.parentNode.removeChild(node);
43557             return;
43558         }
43559         
43560         if (!node.attributes || !node.attributes.length) {
43561             this.cleanUpChildren(node);
43562             return;
43563         }
43564         
43565         function cleanAttr(n,v)
43566         {
43567             
43568             if (v.match(/^\./) || v.match(/^\//)) {
43569                 return;
43570             }
43571             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43572                 return;
43573             }
43574             if (v.match(/^#/)) {
43575                 return;
43576             }
43577 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43578             node.removeAttribute(n);
43579             
43580         }
43581         
43582         var cwhite = this.cwhite;
43583         var cblack = this.cblack;
43584             
43585         function cleanStyle(n,v)
43586         {
43587             if (v.match(/expression/)) { //XSS?? should we even bother..
43588                 node.removeAttribute(n);
43589                 return;
43590             }
43591             
43592             var parts = v.split(/;/);
43593             var clean = [];
43594             
43595             Roo.each(parts, function(p) {
43596                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43597                 if (!p.length) {
43598                     return true;
43599                 }
43600                 var l = p.split(':').shift().replace(/\s+/g,'');
43601                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43602                 
43603                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43604 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43605                     //node.removeAttribute(n);
43606                     return true;
43607                 }
43608                 //Roo.log()
43609                 // only allow 'c whitelisted system attributes'
43610                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43611 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43612                     //node.removeAttribute(n);
43613                     return true;
43614                 }
43615                 
43616                 
43617                  
43618                 
43619                 clean.push(p);
43620                 return true;
43621             });
43622             if (clean.length) { 
43623                 node.setAttribute(n, clean.join(';'));
43624             } else {
43625                 node.removeAttribute(n);
43626             }
43627             
43628         }
43629         
43630         
43631         for (var i = node.attributes.length-1; i > -1 ; i--) {
43632             var a = node.attributes[i];
43633             //console.log(a);
43634             
43635             if (a.name.toLowerCase().substr(0,2)=='on')  {
43636                 node.removeAttribute(a.name);
43637                 continue;
43638             }
43639             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43640                 node.removeAttribute(a.name);
43641                 continue;
43642             }
43643             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43644                 cleanAttr(a.name,a.value); // fixme..
43645                 continue;
43646             }
43647             if (a.name == 'style') {
43648                 cleanStyle(a.name,a.value);
43649                 continue;
43650             }
43651             /// clean up MS crap..
43652             // tecnically this should be a list of valid class'es..
43653             
43654             
43655             if (a.name == 'class') {
43656                 if (a.value.match(/^Mso/)) {
43657                     node.className = '';
43658                 }
43659                 
43660                 if (a.value.match(/body/)) {
43661                     node.className = '';
43662                 }
43663                 continue;
43664             }
43665             
43666             // style cleanup!?
43667             // class cleanup?
43668             
43669         }
43670         
43671         
43672         this.cleanUpChildren(node);
43673         
43674         
43675     },
43676     
43677     /**
43678      * Clean up MS wordisms...
43679      */
43680     cleanWord : function(node)
43681     {
43682         
43683         
43684         if (!node) {
43685             this.cleanWord(this.doc.body);
43686             return;
43687         }
43688         if (node.nodeName == "#text") {
43689             // clean up silly Windows -- stuff?
43690             return; 
43691         }
43692         if (node.nodeName == "#comment") {
43693             node.parentNode.removeChild(node);
43694             // clean up silly Windows -- stuff?
43695             return; 
43696         }
43697         
43698         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43699             node.parentNode.removeChild(node);
43700             return;
43701         }
43702         
43703         // remove - but keep children..
43704         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43705             while (node.childNodes.length) {
43706                 var cn = node.childNodes[0];
43707                 node.removeChild(cn);
43708                 node.parentNode.insertBefore(cn, node);
43709             }
43710             node.parentNode.removeChild(node);
43711             this.iterateChildren(node, this.cleanWord);
43712             return;
43713         }
43714         // clean styles
43715         if (node.className.length) {
43716             
43717             var cn = node.className.split(/\W+/);
43718             var cna = [];
43719             Roo.each(cn, function(cls) {
43720                 if (cls.match(/Mso[a-zA-Z]+/)) {
43721                     return;
43722                 }
43723                 cna.push(cls);
43724             });
43725             node.className = cna.length ? cna.join(' ') : '';
43726             if (!cna.length) {
43727                 node.removeAttribute("class");
43728             }
43729         }
43730         
43731         if (node.hasAttribute("lang")) {
43732             node.removeAttribute("lang");
43733         }
43734         
43735         if (node.hasAttribute("style")) {
43736             
43737             var styles = node.getAttribute("style").split(";");
43738             var nstyle = [];
43739             Roo.each(styles, function(s) {
43740                 if (!s.match(/:/)) {
43741                     return;
43742                 }
43743                 var kv = s.split(":");
43744                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43745                     return;
43746                 }
43747                 // what ever is left... we allow.
43748                 nstyle.push(s);
43749             });
43750             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43751             if (!nstyle.length) {
43752                 node.removeAttribute('style');
43753             }
43754         }
43755         this.iterateChildren(node, this.cleanWord);
43756         
43757         
43758         
43759     },
43760     /**
43761      * iterateChildren of a Node, calling fn each time, using this as the scole..
43762      * @param {DomNode} node node to iterate children of.
43763      * @param {Function} fn method of this class to call on each item.
43764      */
43765     iterateChildren : function(node, fn)
43766     {
43767         if (!node.childNodes.length) {
43768                 return;
43769         }
43770         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43771            fn.call(this, node.childNodes[i])
43772         }
43773     },
43774     
43775     
43776     /**
43777      * cleanTableWidths.
43778      *
43779      * Quite often pasting from word etc.. results in tables with column and widths.
43780      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43781      *
43782      */
43783     cleanTableWidths : function(node)
43784     {
43785          
43786          
43787         if (!node) {
43788             this.cleanTableWidths(this.doc.body);
43789             return;
43790         }
43791         
43792         // ignore list...
43793         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43794             return; 
43795         }
43796         Roo.log(node.tagName);
43797         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43798             this.iterateChildren(node, this.cleanTableWidths);
43799             return;
43800         }
43801         if (node.hasAttribute('width')) {
43802             node.removeAttribute('width');
43803         }
43804         
43805          
43806         if (node.hasAttribute("style")) {
43807             // pretty basic...
43808             
43809             var styles = node.getAttribute("style").split(";");
43810             var nstyle = [];
43811             Roo.each(styles, function(s) {
43812                 if (!s.match(/:/)) {
43813                     return;
43814                 }
43815                 var kv = s.split(":");
43816                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43817                     return;
43818                 }
43819                 // what ever is left... we allow.
43820                 nstyle.push(s);
43821             });
43822             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43823             if (!nstyle.length) {
43824                 node.removeAttribute('style');
43825             }
43826         }
43827         
43828         this.iterateChildren(node, this.cleanTableWidths);
43829         
43830         
43831     },
43832     
43833     
43834     
43835     
43836     domToHTML : function(currentElement, depth, nopadtext) {
43837         
43838         depth = depth || 0;
43839         nopadtext = nopadtext || false;
43840     
43841         if (!currentElement) {
43842             return this.domToHTML(this.doc.body);
43843         }
43844         
43845         //Roo.log(currentElement);
43846         var j;
43847         var allText = false;
43848         var nodeName = currentElement.nodeName;
43849         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43850         
43851         if  (nodeName == '#text') {
43852             
43853             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43854         }
43855         
43856         
43857         var ret = '';
43858         if (nodeName != 'BODY') {
43859              
43860             var i = 0;
43861             // Prints the node tagName, such as <A>, <IMG>, etc
43862             if (tagName) {
43863                 var attr = [];
43864                 for(i = 0; i < currentElement.attributes.length;i++) {
43865                     // quoting?
43866                     var aname = currentElement.attributes.item(i).name;
43867                     if (!currentElement.attributes.item(i).value.length) {
43868                         continue;
43869                     }
43870                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
43871                 }
43872                 
43873                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
43874             } 
43875             else {
43876                 
43877                 // eack
43878             }
43879         } else {
43880             tagName = false;
43881         }
43882         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
43883             return ret;
43884         }
43885         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
43886             nopadtext = true;
43887         }
43888         
43889         
43890         // Traverse the tree
43891         i = 0;
43892         var currentElementChild = currentElement.childNodes.item(i);
43893         var allText = true;
43894         var innerHTML  = '';
43895         lastnode = '';
43896         while (currentElementChild) {
43897             // Formatting code (indent the tree so it looks nice on the screen)
43898             var nopad = nopadtext;
43899             if (lastnode == 'SPAN') {
43900                 nopad  = true;
43901             }
43902             // text
43903             if  (currentElementChild.nodeName == '#text') {
43904                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
43905                 toadd = nopadtext ? toadd : toadd.trim();
43906                 if (!nopad && toadd.length > 80) {
43907                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
43908                 }
43909                 innerHTML  += toadd;
43910                 
43911                 i++;
43912                 currentElementChild = currentElement.childNodes.item(i);
43913                 lastNode = '';
43914                 continue;
43915             }
43916             allText = false;
43917             
43918             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
43919                 
43920             // Recursively traverse the tree structure of the child node
43921             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
43922             lastnode = currentElementChild.nodeName;
43923             i++;
43924             currentElementChild=currentElement.childNodes.item(i);
43925         }
43926         
43927         ret += innerHTML;
43928         
43929         if (!allText) {
43930                 // The remaining code is mostly for formatting the tree
43931             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
43932         }
43933         
43934         
43935         if (tagName) {
43936             ret+= "</"+tagName+">";
43937         }
43938         return ret;
43939         
43940     },
43941         
43942     applyBlacklists : function()
43943     {
43944         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
43945         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
43946         
43947         this.white = [];
43948         this.black = [];
43949         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
43950             if (b.indexOf(tag) > -1) {
43951                 return;
43952             }
43953             this.white.push(tag);
43954             
43955         }, this);
43956         
43957         Roo.each(w, function(tag) {
43958             if (b.indexOf(tag) > -1) {
43959                 return;
43960             }
43961             if (this.white.indexOf(tag) > -1) {
43962                 return;
43963             }
43964             this.white.push(tag);
43965             
43966         }, this);
43967         
43968         
43969         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
43970             if (w.indexOf(tag) > -1) {
43971                 return;
43972             }
43973             this.black.push(tag);
43974             
43975         }, this);
43976         
43977         Roo.each(b, function(tag) {
43978             if (w.indexOf(tag) > -1) {
43979                 return;
43980             }
43981             if (this.black.indexOf(tag) > -1) {
43982                 return;
43983             }
43984             this.black.push(tag);
43985             
43986         }, this);
43987         
43988         
43989         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
43990         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
43991         
43992         this.cwhite = [];
43993         this.cblack = [];
43994         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
43995             if (b.indexOf(tag) > -1) {
43996                 return;
43997             }
43998             this.cwhite.push(tag);
43999             
44000         }, this);
44001         
44002         Roo.each(w, function(tag) {
44003             if (b.indexOf(tag) > -1) {
44004                 return;
44005             }
44006             if (this.cwhite.indexOf(tag) > -1) {
44007                 return;
44008             }
44009             this.cwhite.push(tag);
44010             
44011         }, this);
44012         
44013         
44014         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44015             if (w.indexOf(tag) > -1) {
44016                 return;
44017             }
44018             this.cblack.push(tag);
44019             
44020         }, this);
44021         
44022         Roo.each(b, function(tag) {
44023             if (w.indexOf(tag) > -1) {
44024                 return;
44025             }
44026             if (this.cblack.indexOf(tag) > -1) {
44027                 return;
44028             }
44029             this.cblack.push(tag);
44030             
44031         }, this);
44032     },
44033     
44034     setStylesheets : function(stylesheets)
44035     {
44036         if(typeof(stylesheets) == 'string'){
44037             Roo.get(this.iframe.contentDocument.head).createChild({
44038                 tag : 'link',
44039                 rel : 'stylesheet',
44040                 type : 'text/css',
44041                 href : stylesheets
44042             });
44043             
44044             return;
44045         }
44046         var _this = this;
44047      
44048         Roo.each(stylesheets, function(s) {
44049             if(!s.length){
44050                 return;
44051             }
44052             
44053             Roo.get(_this.iframe.contentDocument.head).createChild({
44054                 tag : 'link',
44055                 rel : 'stylesheet',
44056                 type : 'text/css',
44057                 href : s
44058             });
44059         });
44060
44061         
44062     },
44063     
44064     removeStylesheets : function()
44065     {
44066         var _this = this;
44067         
44068         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44069             s.remove();
44070         });
44071     }
44072     
44073     // hide stuff that is not compatible
44074     /**
44075      * @event blur
44076      * @hide
44077      */
44078     /**
44079      * @event change
44080      * @hide
44081      */
44082     /**
44083      * @event focus
44084      * @hide
44085      */
44086     /**
44087      * @event specialkey
44088      * @hide
44089      */
44090     /**
44091      * @cfg {String} fieldClass @hide
44092      */
44093     /**
44094      * @cfg {String} focusClass @hide
44095      */
44096     /**
44097      * @cfg {String} autoCreate @hide
44098      */
44099     /**
44100      * @cfg {String} inputType @hide
44101      */
44102     /**
44103      * @cfg {String} invalidClass @hide
44104      */
44105     /**
44106      * @cfg {String} invalidText @hide
44107      */
44108     /**
44109      * @cfg {String} msgFx @hide
44110      */
44111     /**
44112      * @cfg {String} validateOnBlur @hide
44113      */
44114 });
44115
44116 Roo.HtmlEditorCore.white = [
44117         'area', 'br', 'img', 'input', 'hr', 'wbr',
44118         
44119        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44120        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44121        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44122        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44123        'table',   'ul',         'xmp', 
44124        
44125        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44126       'thead',   'tr', 
44127      
44128       'dir', 'menu', 'ol', 'ul', 'dl',
44129        
44130       'embed',  'object'
44131 ];
44132
44133
44134 Roo.HtmlEditorCore.black = [
44135     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44136         'applet', // 
44137         'base',   'basefont', 'bgsound', 'blink',  'body', 
44138         'frame',  'frameset', 'head',    'html',   'ilayer', 
44139         'iframe', 'layer',  'link',     'meta',    'object',   
44140         'script', 'style' ,'title',  'xml' // clean later..
44141 ];
44142 Roo.HtmlEditorCore.clean = [
44143     'script', 'style', 'title', 'xml'
44144 ];
44145 Roo.HtmlEditorCore.remove = [
44146     'font'
44147 ];
44148 // attributes..
44149
44150 Roo.HtmlEditorCore.ablack = [
44151     'on'
44152 ];
44153     
44154 Roo.HtmlEditorCore.aclean = [ 
44155     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44156 ];
44157
44158 // protocols..
44159 Roo.HtmlEditorCore.pwhite= [
44160         'http',  'https',  'mailto'
44161 ];
44162
44163 // white listed style attributes.
44164 Roo.HtmlEditorCore.cwhite= [
44165       //  'text-align', /// default is to allow most things..
44166       
44167          
44168 //        'font-size'//??
44169 ];
44170
44171 // black listed style attributes.
44172 Roo.HtmlEditorCore.cblack= [
44173       //  'font-size' -- this can be set by the project 
44174 ];
44175
44176
44177 Roo.HtmlEditorCore.swapCodes   =[ 
44178     [    8211, "--" ], 
44179     [    8212, "--" ], 
44180     [    8216,  "'" ],  
44181     [    8217, "'" ],  
44182     [    8220, '"' ],  
44183     [    8221, '"' ],  
44184     [    8226, "*" ],  
44185     [    8230, "..." ]
44186 ]; 
44187
44188     //<script type="text/javascript">
44189
44190 /*
44191  * Ext JS Library 1.1.1
44192  * Copyright(c) 2006-2007, Ext JS, LLC.
44193  * Licence LGPL
44194  * 
44195  */
44196  
44197  
44198 Roo.form.HtmlEditor = function(config){
44199     
44200     
44201     
44202     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44203     
44204     if (!this.toolbars) {
44205         this.toolbars = [];
44206     }
44207     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44208     
44209     
44210 };
44211
44212 /**
44213  * @class Roo.form.HtmlEditor
44214  * @extends Roo.form.Field
44215  * Provides a lightweight HTML Editor component.
44216  *
44217  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44218  * 
44219  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44220  * supported by this editor.</b><br/><br/>
44221  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44222  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44223  */
44224 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44225     /**
44226      * @cfg {Boolean} clearUp
44227      */
44228     clearUp : true,
44229       /**
44230      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44231      */
44232     toolbars : false,
44233    
44234      /**
44235      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44236      *                        Roo.resizable.
44237      */
44238     resizable : false,
44239      /**
44240      * @cfg {Number} height (in pixels)
44241      */   
44242     height: 300,
44243    /**
44244      * @cfg {Number} width (in pixels)
44245      */   
44246     width: 500,
44247     
44248     /**
44249      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44250      * 
44251      */
44252     stylesheets: false,
44253     
44254     
44255      /**
44256      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44257      * 
44258      */
44259     cblack: false,
44260     /**
44261      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44262      * 
44263      */
44264     cwhite: false,
44265     
44266      /**
44267      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44268      * 
44269      */
44270     black: false,
44271     /**
44272      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44273      * 
44274      */
44275     white: false,
44276     
44277     // id of frame..
44278     frameId: false,
44279     
44280     // private properties
44281     validationEvent : false,
44282     deferHeight: true,
44283     initialized : false,
44284     activated : false,
44285     
44286     onFocus : Roo.emptyFn,
44287     iframePad:3,
44288     hideMode:'offsets',
44289     
44290     actionMode : 'container', // defaults to hiding it...
44291     
44292     defaultAutoCreate : { // modified by initCompnoent..
44293         tag: "textarea",
44294         style:"width:500px;height:300px;",
44295         autocomplete: "new-password"
44296     },
44297
44298     // private
44299     initComponent : function(){
44300         this.addEvents({
44301             /**
44302              * @event initialize
44303              * Fires when the editor is fully initialized (including the iframe)
44304              * @param {HtmlEditor} this
44305              */
44306             initialize: true,
44307             /**
44308              * @event activate
44309              * Fires when the editor is first receives the focus. Any insertion must wait
44310              * until after this event.
44311              * @param {HtmlEditor} this
44312              */
44313             activate: true,
44314              /**
44315              * @event beforesync
44316              * Fires before the textarea is updated with content from the editor iframe. Return false
44317              * to cancel the sync.
44318              * @param {HtmlEditor} this
44319              * @param {String} html
44320              */
44321             beforesync: true,
44322              /**
44323              * @event beforepush
44324              * Fires before the iframe editor is updated with content from the textarea. Return false
44325              * to cancel the push.
44326              * @param {HtmlEditor} this
44327              * @param {String} html
44328              */
44329             beforepush: true,
44330              /**
44331              * @event sync
44332              * Fires when the textarea is updated with content from the editor iframe.
44333              * @param {HtmlEditor} this
44334              * @param {String} html
44335              */
44336             sync: true,
44337              /**
44338              * @event push
44339              * Fires when the iframe editor is updated with content from the textarea.
44340              * @param {HtmlEditor} this
44341              * @param {String} html
44342              */
44343             push: true,
44344              /**
44345              * @event editmodechange
44346              * Fires when the editor switches edit modes
44347              * @param {HtmlEditor} this
44348              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44349              */
44350             editmodechange: true,
44351             /**
44352              * @event editorevent
44353              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44354              * @param {HtmlEditor} this
44355              */
44356             editorevent: true,
44357             /**
44358              * @event firstfocus
44359              * Fires when on first focus - needed by toolbars..
44360              * @param {HtmlEditor} this
44361              */
44362             firstfocus: true,
44363             /**
44364              * @event autosave
44365              * Auto save the htmlEditor value as a file into Events
44366              * @param {HtmlEditor} this
44367              */
44368             autosave: true,
44369             /**
44370              * @event savedpreview
44371              * preview the saved version of htmlEditor
44372              * @param {HtmlEditor} this
44373              */
44374             savedpreview: true,
44375             
44376             /**
44377             * @event stylesheetsclick
44378             * Fires when press the Sytlesheets button
44379             * @param {Roo.HtmlEditorCore} this
44380             */
44381             stylesheetsclick: true
44382         });
44383         this.defaultAutoCreate =  {
44384             tag: "textarea",
44385             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44386             autocomplete: "new-password"
44387         };
44388     },
44389
44390     /**
44391      * Protected method that will not generally be called directly. It
44392      * is called when the editor creates its toolbar. Override this method if you need to
44393      * add custom toolbar buttons.
44394      * @param {HtmlEditor} editor
44395      */
44396     createToolbar : function(editor){
44397         Roo.log("create toolbars");
44398         if (!editor.toolbars || !editor.toolbars.length) {
44399             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44400         }
44401         
44402         for (var i =0 ; i < editor.toolbars.length;i++) {
44403             editor.toolbars[i] = Roo.factory(
44404                     typeof(editor.toolbars[i]) == 'string' ?
44405                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44406                 Roo.form.HtmlEditor);
44407             editor.toolbars[i].init(editor);
44408         }
44409          
44410         
44411     },
44412
44413      
44414     // private
44415     onRender : function(ct, position)
44416     {
44417         var _t = this;
44418         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44419         
44420         this.wrap = this.el.wrap({
44421             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44422         });
44423         
44424         this.editorcore.onRender(ct, position);
44425          
44426         if (this.resizable) {
44427             this.resizeEl = new Roo.Resizable(this.wrap, {
44428                 pinned : true,
44429                 wrap: true,
44430                 dynamic : true,
44431                 minHeight : this.height,
44432                 height: this.height,
44433                 handles : this.resizable,
44434                 width: this.width,
44435                 listeners : {
44436                     resize : function(r, w, h) {
44437                         _t.onResize(w,h); // -something
44438                     }
44439                 }
44440             });
44441             
44442         }
44443         this.createToolbar(this);
44444        
44445         
44446         if(!this.width){
44447             this.setSize(this.wrap.getSize());
44448         }
44449         if (this.resizeEl) {
44450             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44451             // should trigger onReize..
44452         }
44453         
44454         this.keyNav = new Roo.KeyNav(this.el, {
44455             
44456             "tab" : function(e){
44457                 e.preventDefault();
44458                 
44459                 var value = this.getValue();
44460                 
44461                 var start = this.el.dom.selectionStart;
44462                 var end = this.el.dom.selectionEnd;
44463                 
44464                 if(!e.shiftKey){
44465                     
44466                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44467                     this.el.dom.setSelectionRange(end + 1, end + 1);
44468                     return;
44469                 }
44470                 
44471                 var f = value.substring(0, start).split("\t");
44472                 
44473                 if(f.pop().length != 0){
44474                     return;
44475                 }
44476                 
44477                 this.setValue(f.join("\t") + value.substring(end));
44478                 this.el.dom.setSelectionRange(start - 1, start - 1);
44479                 
44480             },
44481             
44482             "home" : function(e){
44483                 e.preventDefault();
44484                 
44485                 var curr = this.el.dom.selectionStart;
44486                 var lines = this.getValue().split("\n");
44487                 
44488                 if(!lines.length){
44489                     return;
44490                 }
44491                 
44492                 if(e.ctrlKey){
44493                     this.el.dom.setSelectionRange(0, 0);
44494                     return;
44495                 }
44496                 
44497                 var pos = 0;
44498                 
44499                 for (var i = 0; i < lines.length;i++) {
44500                     pos += lines[i].length;
44501                     
44502                     if(i != 0){
44503                         pos += 1;
44504                     }
44505                     
44506                     if(pos < curr){
44507                         continue;
44508                     }
44509                     
44510                     pos -= lines[i].length;
44511                     
44512                     break;
44513                 }
44514                 
44515                 if(!e.shiftKey){
44516                     this.el.dom.setSelectionRange(pos, pos);
44517                     return;
44518                 }
44519                 
44520                 this.el.dom.selectionStart = pos;
44521                 this.el.dom.selectionEnd = curr;
44522             },
44523             
44524             "end" : function(e){
44525                 e.preventDefault();
44526                 
44527                 var curr = this.el.dom.selectionStart;
44528                 var lines = this.getValue().split("\n");
44529                 
44530                 if(!lines.length){
44531                     return;
44532                 }
44533                 
44534                 if(e.ctrlKey){
44535                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44536                     return;
44537                 }
44538                 
44539                 var pos = 0;
44540                 
44541                 for (var i = 0; i < lines.length;i++) {
44542                     
44543                     pos += lines[i].length;
44544                     
44545                     if(i != 0){
44546                         pos += 1;
44547                     }
44548                     
44549                     if(pos < curr){
44550                         continue;
44551                     }
44552                     
44553                     break;
44554                 }
44555                 
44556                 if(!e.shiftKey){
44557                     this.el.dom.setSelectionRange(pos, pos);
44558                     return;
44559                 }
44560                 
44561                 this.el.dom.selectionStart = curr;
44562                 this.el.dom.selectionEnd = pos;
44563             },
44564
44565             scope : this,
44566
44567             doRelay : function(foo, bar, hname){
44568                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44569             },
44570
44571             forceKeyDown: true
44572         });
44573         
44574 //        if(this.autosave && this.w){
44575 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44576 //        }
44577     },
44578
44579     // private
44580     onResize : function(w, h)
44581     {
44582         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44583         var ew = false;
44584         var eh = false;
44585         
44586         if(this.el ){
44587             if(typeof w == 'number'){
44588                 var aw = w - this.wrap.getFrameWidth('lr');
44589                 this.el.setWidth(this.adjustWidth('textarea', aw));
44590                 ew = aw;
44591             }
44592             if(typeof h == 'number'){
44593                 var tbh = 0;
44594                 for (var i =0; i < this.toolbars.length;i++) {
44595                     // fixme - ask toolbars for heights?
44596                     tbh += this.toolbars[i].tb.el.getHeight();
44597                     if (this.toolbars[i].footer) {
44598                         tbh += this.toolbars[i].footer.el.getHeight();
44599                     }
44600                 }
44601                 
44602                 
44603                 
44604                 
44605                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44606                 ah -= 5; // knock a few pixes off for look..
44607 //                Roo.log(ah);
44608                 this.el.setHeight(this.adjustWidth('textarea', ah));
44609                 var eh = ah;
44610             }
44611         }
44612         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44613         this.editorcore.onResize(ew,eh);
44614         
44615     },
44616
44617     /**
44618      * Toggles the editor between standard and source edit mode.
44619      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44620      */
44621     toggleSourceEdit : function(sourceEditMode)
44622     {
44623         this.editorcore.toggleSourceEdit(sourceEditMode);
44624         
44625         if(this.editorcore.sourceEditMode){
44626             Roo.log('editor - showing textarea');
44627             
44628 //            Roo.log('in');
44629 //            Roo.log(this.syncValue());
44630             this.editorcore.syncValue();
44631             this.el.removeClass('x-hidden');
44632             this.el.dom.removeAttribute('tabIndex');
44633             this.el.focus();
44634             
44635             for (var i = 0; i < this.toolbars.length; i++) {
44636                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44637                     this.toolbars[i].tb.hide();
44638                     this.toolbars[i].footer.hide();
44639                 }
44640             }
44641             
44642         }else{
44643             Roo.log('editor - hiding textarea');
44644 //            Roo.log('out')
44645 //            Roo.log(this.pushValue()); 
44646             this.editorcore.pushValue();
44647             
44648             this.el.addClass('x-hidden');
44649             this.el.dom.setAttribute('tabIndex', -1);
44650             
44651             for (var i = 0; i < this.toolbars.length; i++) {
44652                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44653                     this.toolbars[i].tb.show();
44654                     this.toolbars[i].footer.show();
44655                 }
44656             }
44657             
44658             //this.deferFocus();
44659         }
44660         
44661         this.setSize(this.wrap.getSize());
44662         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44663         
44664         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44665     },
44666  
44667     // private (for BoxComponent)
44668     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44669
44670     // private (for BoxComponent)
44671     getResizeEl : function(){
44672         return this.wrap;
44673     },
44674
44675     // private (for BoxComponent)
44676     getPositionEl : function(){
44677         return this.wrap;
44678     },
44679
44680     // private
44681     initEvents : function(){
44682         this.originalValue = this.getValue();
44683     },
44684
44685     /**
44686      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44687      * @method
44688      */
44689     markInvalid : Roo.emptyFn,
44690     /**
44691      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44692      * @method
44693      */
44694     clearInvalid : Roo.emptyFn,
44695
44696     setValue : function(v){
44697         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44698         this.editorcore.pushValue();
44699     },
44700
44701      
44702     // private
44703     deferFocus : function(){
44704         this.focus.defer(10, this);
44705     },
44706
44707     // doc'ed in Field
44708     focus : function(){
44709         this.editorcore.focus();
44710         
44711     },
44712       
44713
44714     // private
44715     onDestroy : function(){
44716         
44717         
44718         
44719         if(this.rendered){
44720             
44721             for (var i =0; i < this.toolbars.length;i++) {
44722                 // fixme - ask toolbars for heights?
44723                 this.toolbars[i].onDestroy();
44724             }
44725             
44726             this.wrap.dom.innerHTML = '';
44727             this.wrap.remove();
44728         }
44729     },
44730
44731     // private
44732     onFirstFocus : function(){
44733         //Roo.log("onFirstFocus");
44734         this.editorcore.onFirstFocus();
44735          for (var i =0; i < this.toolbars.length;i++) {
44736             this.toolbars[i].onFirstFocus();
44737         }
44738         
44739     },
44740     
44741     // private
44742     syncValue : function()
44743     {
44744         this.editorcore.syncValue();
44745     },
44746     
44747     pushValue : function()
44748     {
44749         this.editorcore.pushValue();
44750     },
44751     
44752     setStylesheets : function(stylesheets)
44753     {
44754         this.editorcore.setStylesheets(stylesheets);
44755     },
44756     
44757     removeStylesheets : function()
44758     {
44759         this.editorcore.removeStylesheets();
44760     }
44761      
44762     
44763     // hide stuff that is not compatible
44764     /**
44765      * @event blur
44766      * @hide
44767      */
44768     /**
44769      * @event change
44770      * @hide
44771      */
44772     /**
44773      * @event focus
44774      * @hide
44775      */
44776     /**
44777      * @event specialkey
44778      * @hide
44779      */
44780     /**
44781      * @cfg {String} fieldClass @hide
44782      */
44783     /**
44784      * @cfg {String} focusClass @hide
44785      */
44786     /**
44787      * @cfg {String} autoCreate @hide
44788      */
44789     /**
44790      * @cfg {String} inputType @hide
44791      */
44792     /**
44793      * @cfg {String} invalidClass @hide
44794      */
44795     /**
44796      * @cfg {String} invalidText @hide
44797      */
44798     /**
44799      * @cfg {String} msgFx @hide
44800      */
44801     /**
44802      * @cfg {String} validateOnBlur @hide
44803      */
44804 });
44805  
44806     // <script type="text/javascript">
44807 /*
44808  * Based on
44809  * Ext JS Library 1.1.1
44810  * Copyright(c) 2006-2007, Ext JS, LLC.
44811  *  
44812  
44813  */
44814
44815 /**
44816  * @class Roo.form.HtmlEditorToolbar1
44817  * Basic Toolbar
44818  * 
44819  * Usage:
44820  *
44821  new Roo.form.HtmlEditor({
44822     ....
44823     toolbars : [
44824         new Roo.form.HtmlEditorToolbar1({
44825             disable : { fonts: 1 , format: 1, ..., ... , ...],
44826             btns : [ .... ]
44827         })
44828     }
44829      
44830  * 
44831  * @cfg {Object} disable List of elements to disable..
44832  * @cfg {Array} btns List of additional buttons.
44833  * 
44834  * 
44835  * NEEDS Extra CSS? 
44836  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44837  */
44838  
44839 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44840 {
44841     
44842     Roo.apply(this, config);
44843     
44844     // default disabled, based on 'good practice'..
44845     this.disable = this.disable || {};
44846     Roo.applyIf(this.disable, {
44847         fontSize : true,
44848         colors : true,
44849         specialElements : true
44850     });
44851     
44852     
44853     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44854     // dont call parent... till later.
44855 }
44856
44857 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
44858     
44859     tb: false,
44860     
44861     rendered: false,
44862     
44863     editor : false,
44864     editorcore : false,
44865     /**
44866      * @cfg {Object} disable  List of toolbar elements to disable
44867          
44868      */
44869     disable : false,
44870     
44871     
44872      /**
44873      * @cfg {String} createLinkText The default text for the create link prompt
44874      */
44875     createLinkText : 'Please enter the URL for the link:',
44876     /**
44877      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
44878      */
44879     defaultLinkValue : 'http:/'+'/',
44880    
44881     
44882       /**
44883      * @cfg {Array} fontFamilies An array of available font families
44884      */
44885     fontFamilies : [
44886         'Arial',
44887         'Courier New',
44888         'Tahoma',
44889         'Times New Roman',
44890         'Verdana'
44891     ],
44892     
44893     specialChars : [
44894            "&#169;",
44895           "&#174;",     
44896           "&#8482;",    
44897           "&#163;" ,    
44898          // "&#8212;",    
44899           "&#8230;",    
44900           "&#247;" ,    
44901         //  "&#225;" ,     ?? a acute?
44902            "&#8364;"    , //Euro
44903        //   "&#8220;"    ,
44904         //  "&#8221;"    ,
44905         //  "&#8226;"    ,
44906           "&#176;"  //   , // degrees
44907
44908          // "&#233;"     , // e ecute
44909          // "&#250;"     , // u ecute?
44910     ],
44911     
44912     specialElements : [
44913         {
44914             text: "Insert Table",
44915             xtype: 'MenuItem',
44916             xns : Roo.Menu,
44917             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
44918                 
44919         },
44920         {    
44921             text: "Insert Image",
44922             xtype: 'MenuItem',
44923             xns : Roo.Menu,
44924             ihtml : '<img src="about:blank"/>'
44925             
44926         }
44927         
44928          
44929     ],
44930     
44931     
44932     inputElements : [ 
44933             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
44934             "input:submit", "input:button", "select", "textarea", "label" ],
44935     formats : [
44936         ["p"] ,  
44937         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
44938         ["pre"],[ "code"], 
44939         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
44940         ['div'],['span']
44941     ],
44942     
44943     cleanStyles : [
44944         "font-size"
44945     ],
44946      /**
44947      * @cfg {String} defaultFont default font to use.
44948      */
44949     defaultFont: 'tahoma',
44950    
44951     fontSelect : false,
44952     
44953     
44954     formatCombo : false,
44955     
44956     init : function(editor)
44957     {
44958         this.editor = editor;
44959         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44960         var editorcore = this.editorcore;
44961         
44962         var _t = this;
44963         
44964         var fid = editorcore.frameId;
44965         var etb = this;
44966         function btn(id, toggle, handler){
44967             var xid = fid + '-'+ id ;
44968             return {
44969                 id : xid,
44970                 cmd : id,
44971                 cls : 'x-btn-icon x-edit-'+id,
44972                 enableToggle:toggle !== false,
44973                 scope: _t, // was editor...
44974                 handler:handler||_t.relayBtnCmd,
44975                 clickEvent:'mousedown',
44976                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44977                 tabIndex:-1
44978             };
44979         }
44980         
44981         
44982         
44983         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
44984         this.tb = tb;
44985          // stop form submits
44986         tb.el.on('click', function(e){
44987             e.preventDefault(); // what does this do?
44988         });
44989
44990         if(!this.disable.font) { // && !Roo.isSafari){
44991             /* why no safari for fonts 
44992             editor.fontSelect = tb.el.createChild({
44993                 tag:'select',
44994                 tabIndex: -1,
44995                 cls:'x-font-select',
44996                 html: this.createFontOptions()
44997             });
44998             
44999             editor.fontSelect.on('change', function(){
45000                 var font = editor.fontSelect.dom.value;
45001                 editor.relayCmd('fontname', font);
45002                 editor.deferFocus();
45003             }, editor);
45004             
45005             tb.add(
45006                 editor.fontSelect.dom,
45007                 '-'
45008             );
45009             */
45010             
45011         };
45012         if(!this.disable.formats){
45013             this.formatCombo = new Roo.form.ComboBox({
45014                 store: new Roo.data.SimpleStore({
45015                     id : 'tag',
45016                     fields: ['tag'],
45017                     data : this.formats // from states.js
45018                 }),
45019                 blockFocus : true,
45020                 name : '',
45021                 //autoCreate : {tag: "div",  size: "20"},
45022                 displayField:'tag',
45023                 typeAhead: false,
45024                 mode: 'local',
45025                 editable : false,
45026                 triggerAction: 'all',
45027                 emptyText:'Add tag',
45028                 selectOnFocus:true,
45029                 width:135,
45030                 listeners : {
45031                     'select': function(c, r, i) {
45032                         editorcore.insertTag(r.get('tag'));
45033                         editor.focus();
45034                     }
45035                 }
45036
45037             });
45038             tb.addField(this.formatCombo);
45039             
45040         }
45041         
45042         if(!this.disable.format){
45043             tb.add(
45044                 btn('bold'),
45045                 btn('italic'),
45046                 btn('underline'),
45047                 btn('strikethrough')
45048             );
45049         };
45050         if(!this.disable.fontSize){
45051             tb.add(
45052                 '-',
45053                 
45054                 
45055                 btn('increasefontsize', false, editorcore.adjustFont),
45056                 btn('decreasefontsize', false, editorcore.adjustFont)
45057             );
45058         };
45059         
45060         
45061         if(!this.disable.colors){
45062             tb.add(
45063                 '-', {
45064                     id:editorcore.frameId +'-forecolor',
45065                     cls:'x-btn-icon x-edit-forecolor',
45066                     clickEvent:'mousedown',
45067                     tooltip: this.buttonTips['forecolor'] || undefined,
45068                     tabIndex:-1,
45069                     menu : new Roo.menu.ColorMenu({
45070                         allowReselect: true,
45071                         focus: Roo.emptyFn,
45072                         value:'000000',
45073                         plain:true,
45074                         selectHandler: function(cp, color){
45075                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45076                             editor.deferFocus();
45077                         },
45078                         scope: editorcore,
45079                         clickEvent:'mousedown'
45080                     })
45081                 }, {
45082                     id:editorcore.frameId +'backcolor',
45083                     cls:'x-btn-icon x-edit-backcolor',
45084                     clickEvent:'mousedown',
45085                     tooltip: this.buttonTips['backcolor'] || undefined,
45086                     tabIndex:-1,
45087                     menu : new Roo.menu.ColorMenu({
45088                         focus: Roo.emptyFn,
45089                         value:'FFFFFF',
45090                         plain:true,
45091                         allowReselect: true,
45092                         selectHandler: function(cp, color){
45093                             if(Roo.isGecko){
45094                                 editorcore.execCmd('useCSS', false);
45095                                 editorcore.execCmd('hilitecolor', color);
45096                                 editorcore.execCmd('useCSS', true);
45097                                 editor.deferFocus();
45098                             }else{
45099                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45100                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45101                                 editor.deferFocus();
45102                             }
45103                         },
45104                         scope:editorcore,
45105                         clickEvent:'mousedown'
45106                     })
45107                 }
45108             );
45109         };
45110         // now add all the items...
45111         
45112
45113         if(!this.disable.alignments){
45114             tb.add(
45115                 '-',
45116                 btn('justifyleft'),
45117                 btn('justifycenter'),
45118                 btn('justifyright')
45119             );
45120         };
45121
45122         //if(!Roo.isSafari){
45123             if(!this.disable.links){
45124                 tb.add(
45125                     '-',
45126                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45127                 );
45128             };
45129
45130             if(!this.disable.lists){
45131                 tb.add(
45132                     '-',
45133                     btn('insertorderedlist'),
45134                     btn('insertunorderedlist')
45135                 );
45136             }
45137             if(!this.disable.sourceEdit){
45138                 tb.add(
45139                     '-',
45140                     btn('sourceedit', true, function(btn){
45141                         this.toggleSourceEdit(btn.pressed);
45142                     })
45143                 );
45144             }
45145         //}
45146         
45147         var smenu = { };
45148         // special menu.. - needs to be tidied up..
45149         if (!this.disable.special) {
45150             smenu = {
45151                 text: "&#169;",
45152                 cls: 'x-edit-none',
45153                 
45154                 menu : {
45155                     items : []
45156                 }
45157             };
45158             for (var i =0; i < this.specialChars.length; i++) {
45159                 smenu.menu.items.push({
45160                     
45161                     html: this.specialChars[i],
45162                     handler: function(a,b) {
45163                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45164                         //editor.insertAtCursor(a.html);
45165                         
45166                     },
45167                     tabIndex:-1
45168                 });
45169             }
45170             
45171             
45172             tb.add(smenu);
45173             
45174             
45175         }
45176         
45177         var cmenu = { };
45178         if (!this.disable.cleanStyles) {
45179             cmenu = {
45180                 cls: 'x-btn-icon x-btn-clear',
45181                 
45182                 menu : {
45183                     items : []
45184                 }
45185             };
45186             for (var i =0; i < this.cleanStyles.length; i++) {
45187                 cmenu.menu.items.push({
45188                     actiontype : this.cleanStyles[i],
45189                     html: 'Remove ' + this.cleanStyles[i],
45190                     handler: function(a,b) {
45191 //                        Roo.log(a);
45192 //                        Roo.log(b);
45193                         var c = Roo.get(editorcore.doc.body);
45194                         c.select('[style]').each(function(s) {
45195                             s.dom.style.removeProperty(a.actiontype);
45196                         });
45197                         editorcore.syncValue();
45198                     },
45199                     tabIndex:-1
45200                 });
45201             }
45202              cmenu.menu.items.push({
45203                 actiontype : 'tablewidths',
45204                 html: 'Remove Table Widths',
45205                 handler: function(a,b) {
45206                     editorcore.cleanTableWidths();
45207                     editorcore.syncValue();
45208                 },
45209                 tabIndex:-1
45210             });
45211             cmenu.menu.items.push({
45212                 actiontype : 'word',
45213                 html: 'Remove MS Word Formating',
45214                 handler: function(a,b) {
45215                     editorcore.cleanWord();
45216                     editorcore.syncValue();
45217                 },
45218                 tabIndex:-1
45219             });
45220             
45221             cmenu.menu.items.push({
45222                 actiontype : 'all',
45223                 html: 'Remove All Styles',
45224                 handler: function(a,b) {
45225                     
45226                     var c = Roo.get(editorcore.doc.body);
45227                     c.select('[style]').each(function(s) {
45228                         s.dom.removeAttribute('style');
45229                     });
45230                     editorcore.syncValue();
45231                 },
45232                 tabIndex:-1
45233             });
45234             
45235             cmenu.menu.items.push({
45236                 actiontype : 'all',
45237                 html: 'Remove All CSS Classes',
45238                 handler: function(a,b) {
45239                     
45240                     var c = Roo.get(editorcore.doc.body);
45241                     c.select('[class]').each(function(s) {
45242                         s.dom.className = '';
45243                     });
45244                     editorcore.syncValue();
45245                 },
45246                 tabIndex:-1
45247             });
45248             
45249              cmenu.menu.items.push({
45250                 actiontype : 'tidy',
45251                 html: 'Tidy HTML Source',
45252                 handler: function(a,b) {
45253                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45254                     editorcore.syncValue();
45255                 },
45256                 tabIndex:-1
45257             });
45258             
45259             
45260             tb.add(cmenu);
45261         }
45262          
45263         if (!this.disable.specialElements) {
45264             var semenu = {
45265                 text: "Other;",
45266                 cls: 'x-edit-none',
45267                 menu : {
45268                     items : []
45269                 }
45270             };
45271             for (var i =0; i < this.specialElements.length; i++) {
45272                 semenu.menu.items.push(
45273                     Roo.apply({ 
45274                         handler: function(a,b) {
45275                             editor.insertAtCursor(this.ihtml);
45276                         }
45277                     }, this.specialElements[i])
45278                 );
45279                     
45280             }
45281             
45282             tb.add(semenu);
45283             
45284             
45285         }
45286          
45287         
45288         if (this.btns) {
45289             for(var i =0; i< this.btns.length;i++) {
45290                 var b = Roo.factory(this.btns[i],Roo.form);
45291                 b.cls =  'x-edit-none';
45292                 
45293                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45294                     b.cls += ' x-init-enable';
45295                 }
45296                 
45297                 b.scope = editorcore;
45298                 tb.add(b);
45299             }
45300         
45301         }
45302         
45303         
45304         
45305         // disable everything...
45306         
45307         this.tb.items.each(function(item){
45308             
45309            if(
45310                 item.id != editorcore.frameId+ '-sourceedit' && 
45311                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45312             ){
45313                 
45314                 item.disable();
45315             }
45316         });
45317         this.rendered = true;
45318         
45319         // the all the btns;
45320         editor.on('editorevent', this.updateToolbar, this);
45321         // other toolbars need to implement this..
45322         //editor.on('editmodechange', this.updateToolbar, this);
45323     },
45324     
45325     
45326     relayBtnCmd : function(btn) {
45327         this.editorcore.relayCmd(btn.cmd);
45328     },
45329     // private used internally
45330     createLink : function(){
45331         Roo.log("create link?");
45332         var url = prompt(this.createLinkText, this.defaultLinkValue);
45333         if(url && url != 'http:/'+'/'){
45334             this.editorcore.relayCmd('createlink', url);
45335         }
45336     },
45337
45338     
45339     /**
45340      * Protected method that will not generally be called directly. It triggers
45341      * a toolbar update by reading the markup state of the current selection in the editor.
45342      */
45343     updateToolbar: function(){
45344
45345         if(!this.editorcore.activated){
45346             this.editor.onFirstFocus();
45347             return;
45348         }
45349
45350         var btns = this.tb.items.map, 
45351             doc = this.editorcore.doc,
45352             frameId = this.editorcore.frameId;
45353
45354         if(!this.disable.font && !Roo.isSafari){
45355             /*
45356             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45357             if(name != this.fontSelect.dom.value){
45358                 this.fontSelect.dom.value = name;
45359             }
45360             */
45361         }
45362         if(!this.disable.format){
45363             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45364             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45365             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45366             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45367         }
45368         if(!this.disable.alignments){
45369             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45370             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45371             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45372         }
45373         if(!Roo.isSafari && !this.disable.lists){
45374             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45375             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45376         }
45377         
45378         var ans = this.editorcore.getAllAncestors();
45379         if (this.formatCombo) {
45380             
45381             
45382             var store = this.formatCombo.store;
45383             this.formatCombo.setValue("");
45384             for (var i =0; i < ans.length;i++) {
45385                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45386                     // select it..
45387                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45388                     break;
45389                 }
45390             }
45391         }
45392         
45393         
45394         
45395         // hides menus... - so this cant be on a menu...
45396         Roo.menu.MenuMgr.hideAll();
45397
45398         //this.editorsyncValue();
45399     },
45400    
45401     
45402     createFontOptions : function(){
45403         var buf = [], fs = this.fontFamilies, ff, lc;
45404         
45405         
45406         
45407         for(var i = 0, len = fs.length; i< len; i++){
45408             ff = fs[i];
45409             lc = ff.toLowerCase();
45410             buf.push(
45411                 '<option value="',lc,'" style="font-family:',ff,';"',
45412                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45413                     ff,
45414                 '</option>'
45415             );
45416         }
45417         return buf.join('');
45418     },
45419     
45420     toggleSourceEdit : function(sourceEditMode){
45421         
45422         Roo.log("toolbar toogle");
45423         if(sourceEditMode === undefined){
45424             sourceEditMode = !this.sourceEditMode;
45425         }
45426         this.sourceEditMode = sourceEditMode === true;
45427         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45428         // just toggle the button?
45429         if(btn.pressed !== this.sourceEditMode){
45430             btn.toggle(this.sourceEditMode);
45431             return;
45432         }
45433         
45434         if(sourceEditMode){
45435             Roo.log("disabling buttons");
45436             this.tb.items.each(function(item){
45437                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45438                     item.disable();
45439                 }
45440             });
45441           
45442         }else{
45443             Roo.log("enabling buttons");
45444             if(this.editorcore.initialized){
45445                 this.tb.items.each(function(item){
45446                     item.enable();
45447                 });
45448             }
45449             
45450         }
45451         Roo.log("calling toggole on editor");
45452         // tell the editor that it's been pressed..
45453         this.editor.toggleSourceEdit(sourceEditMode);
45454        
45455     },
45456      /**
45457      * Object collection of toolbar tooltips for the buttons in the editor. The key
45458      * is the command id associated with that button and the value is a valid QuickTips object.
45459      * For example:
45460 <pre><code>
45461 {
45462     bold : {
45463         title: 'Bold (Ctrl+B)',
45464         text: 'Make the selected text bold.',
45465         cls: 'x-html-editor-tip'
45466     },
45467     italic : {
45468         title: 'Italic (Ctrl+I)',
45469         text: 'Make the selected text italic.',
45470         cls: 'x-html-editor-tip'
45471     },
45472     ...
45473 </code></pre>
45474     * @type Object
45475      */
45476     buttonTips : {
45477         bold : {
45478             title: 'Bold (Ctrl+B)',
45479             text: 'Make the selected text bold.',
45480             cls: 'x-html-editor-tip'
45481         },
45482         italic : {
45483             title: 'Italic (Ctrl+I)',
45484             text: 'Make the selected text italic.',
45485             cls: 'x-html-editor-tip'
45486         },
45487         underline : {
45488             title: 'Underline (Ctrl+U)',
45489             text: 'Underline the selected text.',
45490             cls: 'x-html-editor-tip'
45491         },
45492         strikethrough : {
45493             title: 'Strikethrough',
45494             text: 'Strikethrough the selected text.',
45495             cls: 'x-html-editor-tip'
45496         },
45497         increasefontsize : {
45498             title: 'Grow Text',
45499             text: 'Increase the font size.',
45500             cls: 'x-html-editor-tip'
45501         },
45502         decreasefontsize : {
45503             title: 'Shrink Text',
45504             text: 'Decrease the font size.',
45505             cls: 'x-html-editor-tip'
45506         },
45507         backcolor : {
45508             title: 'Text Highlight Color',
45509             text: 'Change the background color of the selected text.',
45510             cls: 'x-html-editor-tip'
45511         },
45512         forecolor : {
45513             title: 'Font Color',
45514             text: 'Change the color of the selected text.',
45515             cls: 'x-html-editor-tip'
45516         },
45517         justifyleft : {
45518             title: 'Align Text Left',
45519             text: 'Align text to the left.',
45520             cls: 'x-html-editor-tip'
45521         },
45522         justifycenter : {
45523             title: 'Center Text',
45524             text: 'Center text in the editor.',
45525             cls: 'x-html-editor-tip'
45526         },
45527         justifyright : {
45528             title: 'Align Text Right',
45529             text: 'Align text to the right.',
45530             cls: 'x-html-editor-tip'
45531         },
45532         insertunorderedlist : {
45533             title: 'Bullet List',
45534             text: 'Start a bulleted list.',
45535             cls: 'x-html-editor-tip'
45536         },
45537         insertorderedlist : {
45538             title: 'Numbered List',
45539             text: 'Start a numbered list.',
45540             cls: 'x-html-editor-tip'
45541         },
45542         createlink : {
45543             title: 'Hyperlink',
45544             text: 'Make the selected text a hyperlink.',
45545             cls: 'x-html-editor-tip'
45546         },
45547         sourceedit : {
45548             title: 'Source Edit',
45549             text: 'Switch to source editing mode.',
45550             cls: 'x-html-editor-tip'
45551         }
45552     },
45553     // private
45554     onDestroy : function(){
45555         if(this.rendered){
45556             
45557             this.tb.items.each(function(item){
45558                 if(item.menu){
45559                     item.menu.removeAll();
45560                     if(item.menu.el){
45561                         item.menu.el.destroy();
45562                     }
45563                 }
45564                 item.destroy();
45565             });
45566              
45567         }
45568     },
45569     onFirstFocus: function() {
45570         this.tb.items.each(function(item){
45571            item.enable();
45572         });
45573     }
45574 });
45575
45576
45577
45578
45579 // <script type="text/javascript">
45580 /*
45581  * Based on
45582  * Ext JS Library 1.1.1
45583  * Copyright(c) 2006-2007, Ext JS, LLC.
45584  *  
45585  
45586  */
45587
45588  
45589 /**
45590  * @class Roo.form.HtmlEditor.ToolbarContext
45591  * Context Toolbar
45592  * 
45593  * Usage:
45594  *
45595  new Roo.form.HtmlEditor({
45596     ....
45597     toolbars : [
45598         { xtype: 'ToolbarStandard', styles : {} }
45599         { xtype: 'ToolbarContext', disable : {} }
45600     ]
45601 })
45602
45603      
45604  * 
45605  * @config : {Object} disable List of elements to disable.. (not done yet.)
45606  * @config : {Object} styles  Map of styles available.
45607  * 
45608  */
45609
45610 Roo.form.HtmlEditor.ToolbarContext = function(config)
45611 {
45612     
45613     Roo.apply(this, config);
45614     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45615     // dont call parent... till later.
45616     this.styles = this.styles || {};
45617 }
45618
45619  
45620
45621 Roo.form.HtmlEditor.ToolbarContext.types = {
45622     'IMG' : {
45623         width : {
45624             title: "Width",
45625             width: 40
45626         },
45627         height:  {
45628             title: "Height",
45629             width: 40
45630         },
45631         align: {
45632             title: "Align",
45633             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45634             width : 80
45635             
45636         },
45637         border: {
45638             title: "Border",
45639             width: 40
45640         },
45641         alt: {
45642             title: "Alt",
45643             width: 120
45644         },
45645         src : {
45646             title: "Src",
45647             width: 220
45648         }
45649         
45650     },
45651     'A' : {
45652         name : {
45653             title: "Name",
45654             width: 50
45655         },
45656         target:  {
45657             title: "Target",
45658             width: 120
45659         },
45660         href:  {
45661             title: "Href",
45662             width: 220
45663         } // border?
45664         
45665     },
45666     'TABLE' : {
45667         rows : {
45668             title: "Rows",
45669             width: 20
45670         },
45671         cols : {
45672             title: "Cols",
45673             width: 20
45674         },
45675         width : {
45676             title: "Width",
45677             width: 40
45678         },
45679         height : {
45680             title: "Height",
45681             width: 40
45682         },
45683         border : {
45684             title: "Border",
45685             width: 20
45686         }
45687     },
45688     'TD' : {
45689         width : {
45690             title: "Width",
45691             width: 40
45692         },
45693         height : {
45694             title: "Height",
45695             width: 40
45696         },   
45697         align: {
45698             title: "Align",
45699             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45700             width: 80
45701         },
45702         valign: {
45703             title: "Valign",
45704             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45705             width: 80
45706         },
45707         colspan: {
45708             title: "Colspan",
45709             width: 20
45710             
45711         },
45712          'font-family'  : {
45713             title : "Font",
45714             style : 'fontFamily',
45715             displayField: 'display',
45716             optname : 'font-family',
45717             width: 140
45718         }
45719     },
45720     'INPUT' : {
45721         name : {
45722             title: "name",
45723             width: 120
45724         },
45725         value : {
45726             title: "Value",
45727             width: 120
45728         },
45729         width : {
45730             title: "Width",
45731             width: 40
45732         }
45733     },
45734     'LABEL' : {
45735         'for' : {
45736             title: "For",
45737             width: 120
45738         }
45739     },
45740     'TEXTAREA' : {
45741           name : {
45742             title: "name",
45743             width: 120
45744         },
45745         rows : {
45746             title: "Rows",
45747             width: 20
45748         },
45749         cols : {
45750             title: "Cols",
45751             width: 20
45752         }
45753     },
45754     'SELECT' : {
45755         name : {
45756             title: "name",
45757             width: 120
45758         },
45759         selectoptions : {
45760             title: "Options",
45761             width: 200
45762         }
45763     },
45764     
45765     // should we really allow this??
45766     // should this just be 
45767     'BODY' : {
45768         title : {
45769             title: "Title",
45770             width: 200,
45771             disabled : true
45772         }
45773     },
45774     'SPAN' : {
45775         'font-family'  : {
45776             title : "Font",
45777             style : 'fontFamily',
45778             displayField: 'display',
45779             optname : 'font-family',
45780             width: 140
45781         }
45782     },
45783     'DIV' : {
45784         'font-family'  : {
45785             title : "Font",
45786             style : 'fontFamily',
45787             displayField: 'display',
45788             optname : 'font-family',
45789             width: 140
45790         }
45791     },
45792      'P' : {
45793         'font-family'  : {
45794             title : "Font",
45795             style : 'fontFamily',
45796             displayField: 'display',
45797             optname : 'font-family',
45798             width: 140
45799         }
45800     },
45801     
45802     '*' : {
45803         // empty..
45804     }
45805
45806 };
45807
45808 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45809 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45810
45811 Roo.form.HtmlEditor.ToolbarContext.options = {
45812         'font-family'  : [ 
45813                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45814                 [ 'Courier New', 'Courier New'],
45815                 [ 'Tahoma', 'Tahoma'],
45816                 [ 'Times New Roman,serif', 'Times'],
45817                 [ 'Verdana','Verdana' ]
45818         ]
45819 };
45820
45821 // fixme - these need to be configurable..
45822  
45823
45824 //Roo.form.HtmlEditor.ToolbarContext.types
45825
45826
45827 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45828     
45829     tb: false,
45830     
45831     rendered: false,
45832     
45833     editor : false,
45834     editorcore : false,
45835     /**
45836      * @cfg {Object} disable  List of toolbar elements to disable
45837          
45838      */
45839     disable : false,
45840     /**
45841      * @cfg {Object} styles List of styles 
45842      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45843      *
45844      * These must be defined in the page, so they get rendered correctly..
45845      * .headline { }
45846      * TD.underline { }
45847      * 
45848      */
45849     styles : false,
45850     
45851     options: false,
45852     
45853     toolbars : false,
45854     
45855     init : function(editor)
45856     {
45857         this.editor = editor;
45858         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45859         var editorcore = this.editorcore;
45860         
45861         var fid = editorcore.frameId;
45862         var etb = this;
45863         function btn(id, toggle, handler){
45864             var xid = fid + '-'+ id ;
45865             return {
45866                 id : xid,
45867                 cmd : id,
45868                 cls : 'x-btn-icon x-edit-'+id,
45869                 enableToggle:toggle !== false,
45870                 scope: editorcore, // was editor...
45871                 handler:handler||editorcore.relayBtnCmd,
45872                 clickEvent:'mousedown',
45873                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45874                 tabIndex:-1
45875             };
45876         }
45877         // create a new element.
45878         var wdiv = editor.wrap.createChild({
45879                 tag: 'div'
45880             }, editor.wrap.dom.firstChild.nextSibling, true);
45881         
45882         // can we do this more than once??
45883         
45884          // stop form submits
45885       
45886  
45887         // disable everything...
45888         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
45889         this.toolbars = {};
45890            
45891         for (var i in  ty) {
45892           
45893             this.toolbars[i] = this.buildToolbar(ty[i],i);
45894         }
45895         this.tb = this.toolbars.BODY;
45896         this.tb.el.show();
45897         this.buildFooter();
45898         this.footer.show();
45899         editor.on('hide', function( ) { this.footer.hide() }, this);
45900         editor.on('show', function( ) { this.footer.show() }, this);
45901         
45902          
45903         this.rendered = true;
45904         
45905         // the all the btns;
45906         editor.on('editorevent', this.updateToolbar, this);
45907         // other toolbars need to implement this..
45908         //editor.on('editmodechange', this.updateToolbar, this);
45909     },
45910     
45911     
45912     
45913     /**
45914      * Protected method that will not generally be called directly. It triggers
45915      * a toolbar update by reading the markup state of the current selection in the editor.
45916      *
45917      * Note you can force an update by calling on('editorevent', scope, false)
45918      */
45919     updateToolbar: function(editor,ev,sel){
45920
45921         //Roo.log(ev);
45922         // capture mouse up - this is handy for selecting images..
45923         // perhaps should go somewhere else...
45924         if(!this.editorcore.activated){
45925              this.editor.onFirstFocus();
45926             return;
45927         }
45928         
45929         
45930         
45931         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
45932         // selectNode - might want to handle IE?
45933         if (ev &&
45934             (ev.type == 'mouseup' || ev.type == 'click' ) &&
45935             ev.target && ev.target.tagName == 'IMG') {
45936             // they have click on an image...
45937             // let's see if we can change the selection...
45938             sel = ev.target;
45939          
45940               var nodeRange = sel.ownerDocument.createRange();
45941             try {
45942                 nodeRange.selectNode(sel);
45943             } catch (e) {
45944                 nodeRange.selectNodeContents(sel);
45945             }
45946             //nodeRange.collapse(true);
45947             var s = this.editorcore.win.getSelection();
45948             s.removeAllRanges();
45949             s.addRange(nodeRange);
45950         }  
45951         
45952       
45953         var updateFooter = sel ? false : true;
45954         
45955         
45956         var ans = this.editorcore.getAllAncestors();
45957         
45958         // pick
45959         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
45960         
45961         if (!sel) { 
45962             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
45963             sel = sel ? sel : this.editorcore.doc.body;
45964             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
45965             
45966         }
45967         // pick a menu that exists..
45968         var tn = sel.tagName.toUpperCase();
45969         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
45970         
45971         tn = sel.tagName.toUpperCase();
45972         
45973         var lastSel = this.tb.selectedNode;
45974         
45975         this.tb.selectedNode = sel;
45976         
45977         // if current menu does not match..
45978         
45979         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
45980                 
45981             this.tb.el.hide();
45982             ///console.log("show: " + tn);
45983             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
45984             this.tb.el.show();
45985             // update name
45986             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
45987             
45988             
45989             // update attributes
45990             if (this.tb.fields) {
45991                 this.tb.fields.each(function(e) {
45992                     if (e.stylename) {
45993                         e.setValue(sel.style[e.stylename]);
45994                         return;
45995                     } 
45996                    e.setValue(sel.getAttribute(e.attrname));
45997                 });
45998             }
45999             
46000             var hasStyles = false;
46001             for(var i in this.styles) {
46002                 hasStyles = true;
46003                 break;
46004             }
46005             
46006             // update styles
46007             if (hasStyles) { 
46008                 var st = this.tb.fields.item(0);
46009                 
46010                 st.store.removeAll();
46011                
46012                 
46013                 var cn = sel.className.split(/\s+/);
46014                 
46015                 var avs = [];
46016                 if (this.styles['*']) {
46017                     
46018                     Roo.each(this.styles['*'], function(v) {
46019                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46020                     });
46021                 }
46022                 if (this.styles[tn]) { 
46023                     Roo.each(this.styles[tn], function(v) {
46024                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46025                     });
46026                 }
46027                 
46028                 st.store.loadData(avs);
46029                 st.collapse();
46030                 st.setValue(cn);
46031             }
46032             // flag our selected Node.
46033             this.tb.selectedNode = sel;
46034            
46035            
46036             Roo.menu.MenuMgr.hideAll();
46037
46038         }
46039         
46040         if (!updateFooter) {
46041             //this.footDisp.dom.innerHTML = ''; 
46042             return;
46043         }
46044         // update the footer
46045         //
46046         var html = '';
46047         
46048         this.footerEls = ans.reverse();
46049         Roo.each(this.footerEls, function(a,i) {
46050             if (!a) { return; }
46051             html += html.length ? ' &gt; '  :  '';
46052             
46053             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46054             
46055         });
46056        
46057         // 
46058         var sz = this.footDisp.up('td').getSize();
46059         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46060         this.footDisp.dom.style.marginLeft = '5px';
46061         
46062         this.footDisp.dom.style.overflow = 'hidden';
46063         
46064         this.footDisp.dom.innerHTML = html;
46065             
46066         //this.editorsyncValue();
46067     },
46068      
46069     
46070    
46071        
46072     // private
46073     onDestroy : function(){
46074         if(this.rendered){
46075             
46076             this.tb.items.each(function(item){
46077                 if(item.menu){
46078                     item.menu.removeAll();
46079                     if(item.menu.el){
46080                         item.menu.el.destroy();
46081                     }
46082                 }
46083                 item.destroy();
46084             });
46085              
46086         }
46087     },
46088     onFirstFocus: function() {
46089         // need to do this for all the toolbars..
46090         this.tb.items.each(function(item){
46091            item.enable();
46092         });
46093     },
46094     buildToolbar: function(tlist, nm)
46095     {
46096         var editor = this.editor;
46097         var editorcore = this.editorcore;
46098          // create a new element.
46099         var wdiv = editor.wrap.createChild({
46100                 tag: 'div'
46101             }, editor.wrap.dom.firstChild.nextSibling, true);
46102         
46103        
46104         var tb = new Roo.Toolbar(wdiv);
46105         // add the name..
46106         
46107         tb.add(nm+ ":&nbsp;");
46108         
46109         var styles = [];
46110         for(var i in this.styles) {
46111             styles.push(i);
46112         }
46113         
46114         // styles...
46115         if (styles && styles.length) {
46116             
46117             // this needs a multi-select checkbox...
46118             tb.addField( new Roo.form.ComboBox({
46119                 store: new Roo.data.SimpleStore({
46120                     id : 'val',
46121                     fields: ['val', 'selected'],
46122                     data : [] 
46123                 }),
46124                 name : '-roo-edit-className',
46125                 attrname : 'className',
46126                 displayField: 'val',
46127                 typeAhead: false,
46128                 mode: 'local',
46129                 editable : false,
46130                 triggerAction: 'all',
46131                 emptyText:'Select Style',
46132                 selectOnFocus:true,
46133                 width: 130,
46134                 listeners : {
46135                     'select': function(c, r, i) {
46136                         // initial support only for on class per el..
46137                         tb.selectedNode.className =  r ? r.get('val') : '';
46138                         editorcore.syncValue();
46139                     }
46140                 }
46141     
46142             }));
46143         }
46144         
46145         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46146         var tbops = tbc.options;
46147         
46148         for (var i in tlist) {
46149             
46150             var item = tlist[i];
46151             tb.add(item.title + ":&nbsp;");
46152             
46153             
46154             //optname == used so you can configure the options available..
46155             var opts = item.opts ? item.opts : false;
46156             if (item.optname) {
46157                 opts = tbops[item.optname];
46158            
46159             }
46160             
46161             if (opts) {
46162                 // opts == pulldown..
46163                 tb.addField( new Roo.form.ComboBox({
46164                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46165                         id : 'val',
46166                         fields: ['val', 'display'],
46167                         data : opts  
46168                     }),
46169                     name : '-roo-edit-' + i,
46170                     attrname : i,
46171                     stylename : item.style ? item.style : false,
46172                     displayField: item.displayField ? item.displayField : 'val',
46173                     valueField :  'val',
46174                     typeAhead: false,
46175                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46176                     editable : false,
46177                     triggerAction: 'all',
46178                     emptyText:'Select',
46179                     selectOnFocus:true,
46180                     width: item.width ? item.width  : 130,
46181                     listeners : {
46182                         'select': function(c, r, i) {
46183                             if (c.stylename) {
46184                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46185                                 return;
46186                             }
46187                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46188                         }
46189                     }
46190
46191                 }));
46192                 continue;
46193                     
46194                  
46195                 
46196                 tb.addField( new Roo.form.TextField({
46197                     name: i,
46198                     width: 100,
46199                     //allowBlank:false,
46200                     value: ''
46201                 }));
46202                 continue;
46203             }
46204             tb.addField( new Roo.form.TextField({
46205                 name: '-roo-edit-' + i,
46206                 attrname : i,
46207                 
46208                 width: item.width,
46209                 //allowBlank:true,
46210                 value: '',
46211                 listeners: {
46212                     'change' : function(f, nv, ov) {
46213                         tb.selectedNode.setAttribute(f.attrname, nv);
46214                         editorcore.syncValue();
46215                     }
46216                 }
46217             }));
46218              
46219         }
46220         
46221         var _this = this;
46222         
46223         if(nm == 'BODY'){
46224             tb.addSeparator();
46225         
46226             tb.addButton( {
46227                 text: 'Stylesheets',
46228
46229                 listeners : {
46230                     click : function ()
46231                     {
46232                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46233                     }
46234                 }
46235             });
46236         }
46237         
46238         tb.addFill();
46239         tb.addButton( {
46240             text: 'Remove Tag',
46241     
46242             listeners : {
46243                 click : function ()
46244                 {
46245                     // remove
46246                     // undo does not work.
46247                      
46248                     var sn = tb.selectedNode;
46249                     
46250                     var pn = sn.parentNode;
46251                     
46252                     var stn =  sn.childNodes[0];
46253                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46254                     while (sn.childNodes.length) {
46255                         var node = sn.childNodes[0];
46256                         sn.removeChild(node);
46257                         //Roo.log(node);
46258                         pn.insertBefore(node, sn);
46259                         
46260                     }
46261                     pn.removeChild(sn);
46262                     var range = editorcore.createRange();
46263         
46264                     range.setStart(stn,0);
46265                     range.setEnd(en,0); //????
46266                     //range.selectNode(sel);
46267                     
46268                     
46269                     var selection = editorcore.getSelection();
46270                     selection.removeAllRanges();
46271                     selection.addRange(range);
46272                     
46273                     
46274                     
46275                     //_this.updateToolbar(null, null, pn);
46276                     _this.updateToolbar(null, null, null);
46277                     _this.footDisp.dom.innerHTML = ''; 
46278                 }
46279             }
46280             
46281                     
46282                 
46283             
46284         });
46285         
46286         
46287         tb.el.on('click', function(e){
46288             e.preventDefault(); // what does this do?
46289         });
46290         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46291         tb.el.hide();
46292         tb.name = nm;
46293         // dont need to disable them... as they will get hidden
46294         return tb;
46295          
46296         
46297     },
46298     buildFooter : function()
46299     {
46300         
46301         var fel = this.editor.wrap.createChild();
46302         this.footer = new Roo.Toolbar(fel);
46303         // toolbar has scrolly on left / right?
46304         var footDisp= new Roo.Toolbar.Fill();
46305         var _t = this;
46306         this.footer.add(
46307             {
46308                 text : '&lt;',
46309                 xtype: 'Button',
46310                 handler : function() {
46311                     _t.footDisp.scrollTo('left',0,true)
46312                 }
46313             }
46314         );
46315         this.footer.add( footDisp );
46316         this.footer.add( 
46317             {
46318                 text : '&gt;',
46319                 xtype: 'Button',
46320                 handler : function() {
46321                     // no animation..
46322                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46323                 }
46324             }
46325         );
46326         var fel = Roo.get(footDisp.el);
46327         fel.addClass('x-editor-context');
46328         this.footDispWrap = fel; 
46329         this.footDispWrap.overflow  = 'hidden';
46330         
46331         this.footDisp = fel.createChild();
46332         this.footDispWrap.on('click', this.onContextClick, this)
46333         
46334         
46335     },
46336     onContextClick : function (ev,dom)
46337     {
46338         ev.preventDefault();
46339         var  cn = dom.className;
46340         //Roo.log(cn);
46341         if (!cn.match(/x-ed-loc-/)) {
46342             return;
46343         }
46344         var n = cn.split('-').pop();
46345         var ans = this.footerEls;
46346         var sel = ans[n];
46347         
46348          // pick
46349         var range = this.editorcore.createRange();
46350         
46351         range.selectNodeContents(sel);
46352         //range.selectNode(sel);
46353         
46354         
46355         var selection = this.editorcore.getSelection();
46356         selection.removeAllRanges();
46357         selection.addRange(range);
46358         
46359         
46360         
46361         this.updateToolbar(null, null, sel);
46362         
46363         
46364     }
46365     
46366     
46367     
46368     
46369     
46370 });
46371
46372
46373
46374
46375
46376 /*
46377  * Based on:
46378  * Ext JS Library 1.1.1
46379  * Copyright(c) 2006-2007, Ext JS, LLC.
46380  *
46381  * Originally Released Under LGPL - original licence link has changed is not relivant.
46382  *
46383  * Fork - LGPL
46384  * <script type="text/javascript">
46385  */
46386  
46387 /**
46388  * @class Roo.form.BasicForm
46389  * @extends Roo.util.Observable
46390  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46391  * @constructor
46392  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46393  * @param {Object} config Configuration options
46394  */
46395 Roo.form.BasicForm = function(el, config){
46396     this.allItems = [];
46397     this.childForms = [];
46398     Roo.apply(this, config);
46399     /*
46400      * The Roo.form.Field items in this form.
46401      * @type MixedCollection
46402      */
46403      
46404      
46405     this.items = new Roo.util.MixedCollection(false, function(o){
46406         return o.id || (o.id = Roo.id());
46407     });
46408     this.addEvents({
46409         /**
46410          * @event beforeaction
46411          * Fires before any action is performed. Return false to cancel the action.
46412          * @param {Form} this
46413          * @param {Action} action The action to be performed
46414          */
46415         beforeaction: true,
46416         /**
46417          * @event actionfailed
46418          * Fires when an action fails.
46419          * @param {Form} this
46420          * @param {Action} action The action that failed
46421          */
46422         actionfailed : true,
46423         /**
46424          * @event actioncomplete
46425          * Fires when an action is completed.
46426          * @param {Form} this
46427          * @param {Action} action The action that completed
46428          */
46429         actioncomplete : true
46430     });
46431     if(el){
46432         this.initEl(el);
46433     }
46434     Roo.form.BasicForm.superclass.constructor.call(this);
46435 };
46436
46437 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46438     /**
46439      * @cfg {String} method
46440      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46441      */
46442     /**
46443      * @cfg {DataReader} reader
46444      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46445      * This is optional as there is built-in support for processing JSON.
46446      */
46447     /**
46448      * @cfg {DataReader} errorReader
46449      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46450      * This is completely optional as there is built-in support for processing JSON.
46451      */
46452     /**
46453      * @cfg {String} url
46454      * The URL to use for form actions if one isn't supplied in the action options.
46455      */
46456     /**
46457      * @cfg {Boolean} fileUpload
46458      * Set to true if this form is a file upload.
46459      */
46460      
46461     /**
46462      * @cfg {Object} baseParams
46463      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46464      */
46465      /**
46466      
46467     /**
46468      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46469      */
46470     timeout: 30,
46471
46472     // private
46473     activeAction : null,
46474
46475     /**
46476      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46477      * or setValues() data instead of when the form was first created.
46478      */
46479     trackResetOnLoad : false,
46480     
46481     
46482     /**
46483      * childForms - used for multi-tab forms
46484      * @type {Array}
46485      */
46486     childForms : false,
46487     
46488     /**
46489      * allItems - full list of fields.
46490      * @type {Array}
46491      */
46492     allItems : false,
46493     
46494     /**
46495      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46496      * element by passing it or its id or mask the form itself by passing in true.
46497      * @type Mixed
46498      */
46499     waitMsgTarget : false,
46500
46501     // private
46502     initEl : function(el){
46503         this.el = Roo.get(el);
46504         this.id = this.el.id || Roo.id();
46505         this.el.on('submit', this.onSubmit, this);
46506         this.el.addClass('x-form');
46507     },
46508
46509     // private
46510     onSubmit : function(e){
46511         e.stopEvent();
46512     },
46513
46514     /**
46515      * Returns true if client-side validation on the form is successful.
46516      * @return Boolean
46517      */
46518     isValid : function(){
46519         var valid = true;
46520         this.items.each(function(f){
46521            if(!f.validate()){
46522                valid = false;
46523            }
46524         });
46525         return valid;
46526     },
46527
46528     /**
46529      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46530      * @return Boolean
46531      */
46532     isDirty : function(){
46533         var dirty = false;
46534         this.items.each(function(f){
46535            if(f.isDirty()){
46536                dirty = true;
46537                return false;
46538            }
46539         });
46540         return dirty;
46541     },
46542     
46543     /**
46544      * Returns true if any fields in this form have changed since their original load. (New version)
46545      * @return Boolean
46546      */
46547     
46548     hasChanged : function()
46549     {
46550         var dirty = false;
46551         this.items.each(function(f){
46552            if(f.hasChanged()){
46553                dirty = true;
46554                return false;
46555            }
46556         });
46557         return dirty;
46558         
46559     },
46560     /**
46561      * Resets all hasChanged to 'false' -
46562      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46563      * So hasChanged storage is only to be used for this purpose
46564      * @return Boolean
46565      */
46566     resetHasChanged : function()
46567     {
46568         this.items.each(function(f){
46569            f.resetHasChanged();
46570         });
46571         
46572     },
46573     
46574     
46575     /**
46576      * Performs a predefined action (submit or load) or custom actions you define on this form.
46577      * @param {String} actionName The name of the action type
46578      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46579      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46580      * accept other config options):
46581      * <pre>
46582 Property          Type             Description
46583 ----------------  ---------------  ----------------------------------------------------------------------------------
46584 url               String           The url for the action (defaults to the form's url)
46585 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46586 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46587 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46588                                    validate the form on the client (defaults to false)
46589      * </pre>
46590      * @return {BasicForm} this
46591      */
46592     doAction : function(action, options){
46593         if(typeof action == 'string'){
46594             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46595         }
46596         if(this.fireEvent('beforeaction', this, action) !== false){
46597             this.beforeAction(action);
46598             action.run.defer(100, action);
46599         }
46600         return this;
46601     },
46602
46603     /**
46604      * Shortcut to do a submit action.
46605      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46606      * @return {BasicForm} this
46607      */
46608     submit : function(options){
46609         this.doAction('submit', options);
46610         return this;
46611     },
46612
46613     /**
46614      * Shortcut to do a load action.
46615      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46616      * @return {BasicForm} this
46617      */
46618     load : function(options){
46619         this.doAction('load', options);
46620         return this;
46621     },
46622
46623     /**
46624      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46625      * @param {Record} record The record to edit
46626      * @return {BasicForm} this
46627      */
46628     updateRecord : function(record){
46629         record.beginEdit();
46630         var fs = record.fields;
46631         fs.each(function(f){
46632             var field = this.findField(f.name);
46633             if(field){
46634                 record.set(f.name, field.getValue());
46635             }
46636         }, this);
46637         record.endEdit();
46638         return this;
46639     },
46640
46641     /**
46642      * Loads an Roo.data.Record into this form.
46643      * @param {Record} record The record to load
46644      * @return {BasicForm} this
46645      */
46646     loadRecord : function(record){
46647         this.setValues(record.data);
46648         return this;
46649     },
46650
46651     // private
46652     beforeAction : function(action){
46653         var o = action.options;
46654         
46655        
46656         if(this.waitMsgTarget === true){
46657             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46658         }else if(this.waitMsgTarget){
46659             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46660             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46661         }else {
46662             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46663         }
46664          
46665     },
46666
46667     // private
46668     afterAction : function(action, success){
46669         this.activeAction = null;
46670         var o = action.options;
46671         
46672         if(this.waitMsgTarget === true){
46673             this.el.unmask();
46674         }else if(this.waitMsgTarget){
46675             this.waitMsgTarget.unmask();
46676         }else{
46677             Roo.MessageBox.updateProgress(1);
46678             Roo.MessageBox.hide();
46679         }
46680          
46681         if(success){
46682             if(o.reset){
46683                 this.reset();
46684             }
46685             Roo.callback(o.success, o.scope, [this, action]);
46686             this.fireEvent('actioncomplete', this, action);
46687             
46688         }else{
46689             
46690             // failure condition..
46691             // we have a scenario where updates need confirming.
46692             // eg. if a locking scenario exists..
46693             // we look for { errors : { needs_confirm : true }} in the response.
46694             if (
46695                 (typeof(action.result) != 'undefined')  &&
46696                 (typeof(action.result.errors) != 'undefined')  &&
46697                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46698            ){
46699                 var _t = this;
46700                 Roo.MessageBox.confirm(
46701                     "Change requires confirmation",
46702                     action.result.errorMsg,
46703                     function(r) {
46704                         if (r != 'yes') {
46705                             return;
46706                         }
46707                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46708                     }
46709                     
46710                 );
46711                 
46712                 
46713                 
46714                 return;
46715             }
46716             
46717             Roo.callback(o.failure, o.scope, [this, action]);
46718             // show an error message if no failed handler is set..
46719             if (!this.hasListener('actionfailed')) {
46720                 Roo.MessageBox.alert("Error",
46721                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46722                         action.result.errorMsg :
46723                         "Saving Failed, please check your entries or try again"
46724                 );
46725             }
46726             
46727             this.fireEvent('actionfailed', this, action);
46728         }
46729         
46730     },
46731
46732     /**
46733      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46734      * @param {String} id The value to search for
46735      * @return Field
46736      */
46737     findField : function(id){
46738         var field = this.items.get(id);
46739         if(!field){
46740             this.items.each(function(f){
46741                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46742                     field = f;
46743                     return false;
46744                 }
46745             });
46746         }
46747         return field || null;
46748     },
46749
46750     /**
46751      * Add a secondary form to this one, 
46752      * Used to provide tabbed forms. One form is primary, with hidden values 
46753      * which mirror the elements from the other forms.
46754      * 
46755      * @param {Roo.form.Form} form to add.
46756      * 
46757      */
46758     addForm : function(form)
46759     {
46760        
46761         if (this.childForms.indexOf(form) > -1) {
46762             // already added..
46763             return;
46764         }
46765         this.childForms.push(form);
46766         var n = '';
46767         Roo.each(form.allItems, function (fe) {
46768             
46769             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46770             if (this.findField(n)) { // already added..
46771                 return;
46772             }
46773             var add = new Roo.form.Hidden({
46774                 name : n
46775             });
46776             add.render(this.el);
46777             
46778             this.add( add );
46779         }, this);
46780         
46781     },
46782     /**
46783      * Mark fields in this form invalid in bulk.
46784      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46785      * @return {BasicForm} this
46786      */
46787     markInvalid : function(errors){
46788         if(errors instanceof Array){
46789             for(var i = 0, len = errors.length; i < len; i++){
46790                 var fieldError = errors[i];
46791                 var f = this.findField(fieldError.id);
46792                 if(f){
46793                     f.markInvalid(fieldError.msg);
46794                 }
46795             }
46796         }else{
46797             var field, id;
46798             for(id in errors){
46799                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46800                     field.markInvalid(errors[id]);
46801                 }
46802             }
46803         }
46804         Roo.each(this.childForms || [], function (f) {
46805             f.markInvalid(errors);
46806         });
46807         
46808         return this;
46809     },
46810
46811     /**
46812      * Set values for fields in this form in bulk.
46813      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46814      * @return {BasicForm} this
46815      */
46816     setValues : function(values){
46817         if(values instanceof Array){ // array of objects
46818             for(var i = 0, len = values.length; i < len; i++){
46819                 var v = values[i];
46820                 var f = this.findField(v.id);
46821                 if(f){
46822                     f.setValue(v.value);
46823                     if(this.trackResetOnLoad){
46824                         f.originalValue = f.getValue();
46825                     }
46826                 }
46827             }
46828         }else{ // object hash
46829             var field, id;
46830             for(id in values){
46831                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46832                     
46833                     if (field.setFromData && 
46834                         field.valueField && 
46835                         field.displayField &&
46836                         // combos' with local stores can 
46837                         // be queried via setValue()
46838                         // to set their value..
46839                         (field.store && !field.store.isLocal)
46840                         ) {
46841                         // it's a combo
46842                         var sd = { };
46843                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46844                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46845                         field.setFromData(sd);
46846                         
46847                     } else {
46848                         field.setValue(values[id]);
46849                     }
46850                     
46851                     
46852                     if(this.trackResetOnLoad){
46853                         field.originalValue = field.getValue();
46854                     }
46855                 }
46856             }
46857         }
46858         this.resetHasChanged();
46859         
46860         
46861         Roo.each(this.childForms || [], function (f) {
46862             f.setValues(values);
46863             f.resetHasChanged();
46864         });
46865                 
46866         return this;
46867     },
46868
46869     /**
46870      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
46871      * they are returned as an array.
46872      * @param {Boolean} asString
46873      * @return {Object}
46874      */
46875     getValues : function(asString){
46876         if (this.childForms) {
46877             // copy values from the child forms
46878             Roo.each(this.childForms, function (f) {
46879                 this.setValues(f.getValues());
46880             }, this);
46881         }
46882         
46883         
46884         
46885         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
46886         if(asString === true){
46887             return fs;
46888         }
46889         return Roo.urlDecode(fs);
46890     },
46891     
46892     /**
46893      * Returns the fields in this form as an object with key/value pairs. 
46894      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
46895      * @return {Object}
46896      */
46897     getFieldValues : function(with_hidden)
46898     {
46899         if (this.childForms) {
46900             // copy values from the child forms
46901             // should this call getFieldValues - probably not as we do not currently copy
46902             // hidden fields when we generate..
46903             Roo.each(this.childForms, function (f) {
46904                 this.setValues(f.getValues());
46905             }, this);
46906         }
46907         
46908         var ret = {};
46909         this.items.each(function(f){
46910             if (!f.getName()) {
46911                 return;
46912             }
46913             var v = f.getValue();
46914             if (f.inputType =='radio') {
46915                 if (typeof(ret[f.getName()]) == 'undefined') {
46916                     ret[f.getName()] = ''; // empty..
46917                 }
46918                 
46919                 if (!f.el.dom.checked) {
46920                     return;
46921                     
46922                 }
46923                 v = f.el.dom.value;
46924                 
46925             }
46926             
46927             // not sure if this supported any more..
46928             if ((typeof(v) == 'object') && f.getRawValue) {
46929                 v = f.getRawValue() ; // dates..
46930             }
46931             // combo boxes where name != hiddenName...
46932             if (f.name != f.getName()) {
46933                 ret[f.name] = f.getRawValue();
46934             }
46935             ret[f.getName()] = v;
46936         });
46937         
46938         return ret;
46939     },
46940
46941     /**
46942      * Clears all invalid messages in this form.
46943      * @return {BasicForm} this
46944      */
46945     clearInvalid : function(){
46946         this.items.each(function(f){
46947            f.clearInvalid();
46948         });
46949         
46950         Roo.each(this.childForms || [], function (f) {
46951             f.clearInvalid();
46952         });
46953         
46954         
46955         return this;
46956     },
46957
46958     /**
46959      * Resets this form.
46960      * @return {BasicForm} this
46961      */
46962     reset : function(){
46963         this.items.each(function(f){
46964             f.reset();
46965         });
46966         
46967         Roo.each(this.childForms || [], function (f) {
46968             f.reset();
46969         });
46970         this.resetHasChanged();
46971         
46972         return this;
46973     },
46974
46975     /**
46976      * Add Roo.form components to this form.
46977      * @param {Field} field1
46978      * @param {Field} field2 (optional)
46979      * @param {Field} etc (optional)
46980      * @return {BasicForm} this
46981      */
46982     add : function(){
46983         this.items.addAll(Array.prototype.slice.call(arguments, 0));
46984         return this;
46985     },
46986
46987
46988     /**
46989      * Removes a field from the items collection (does NOT remove its markup).
46990      * @param {Field} field
46991      * @return {BasicForm} this
46992      */
46993     remove : function(field){
46994         this.items.remove(field);
46995         return this;
46996     },
46997
46998     /**
46999      * Looks at the fields in this form, checks them for an id attribute,
47000      * and calls applyTo on the existing dom element with that id.
47001      * @return {BasicForm} this
47002      */
47003     render : function(){
47004         this.items.each(function(f){
47005             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47006                 f.applyTo(f.id);
47007             }
47008         });
47009         return this;
47010     },
47011
47012     /**
47013      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47014      * @param {Object} values
47015      * @return {BasicForm} this
47016      */
47017     applyToFields : function(o){
47018         this.items.each(function(f){
47019            Roo.apply(f, o);
47020         });
47021         return this;
47022     },
47023
47024     /**
47025      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47026      * @param {Object} values
47027      * @return {BasicForm} this
47028      */
47029     applyIfToFields : function(o){
47030         this.items.each(function(f){
47031            Roo.applyIf(f, o);
47032         });
47033         return this;
47034     }
47035 });
47036
47037 // back compat
47038 Roo.BasicForm = Roo.form.BasicForm;/*
47039  * Based on:
47040  * Ext JS Library 1.1.1
47041  * Copyright(c) 2006-2007, Ext JS, LLC.
47042  *
47043  * Originally Released Under LGPL - original licence link has changed is not relivant.
47044  *
47045  * Fork - LGPL
47046  * <script type="text/javascript">
47047  */
47048
47049 /**
47050  * @class Roo.form.Form
47051  * @extends Roo.form.BasicForm
47052  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47053  * @constructor
47054  * @param {Object} config Configuration options
47055  */
47056 Roo.form.Form = function(config){
47057     var xitems =  [];
47058     if (config.items) {
47059         xitems = config.items;
47060         delete config.items;
47061     }
47062    
47063     
47064     Roo.form.Form.superclass.constructor.call(this, null, config);
47065     this.url = this.url || this.action;
47066     if(!this.root){
47067         this.root = new Roo.form.Layout(Roo.applyIf({
47068             id: Roo.id()
47069         }, config));
47070     }
47071     this.active = this.root;
47072     /**
47073      * Array of all the buttons that have been added to this form via {@link addButton}
47074      * @type Array
47075      */
47076     this.buttons = [];
47077     this.allItems = [];
47078     this.addEvents({
47079         /**
47080          * @event clientvalidation
47081          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47082          * @param {Form} this
47083          * @param {Boolean} valid true if the form has passed client-side validation
47084          */
47085         clientvalidation: true,
47086         /**
47087          * @event rendered
47088          * Fires when the form is rendered
47089          * @param {Roo.form.Form} form
47090          */
47091         rendered : true
47092     });
47093     
47094     if (this.progressUrl) {
47095             // push a hidden field onto the list of fields..
47096             this.addxtype( {
47097                     xns: Roo.form, 
47098                     xtype : 'Hidden', 
47099                     name : 'UPLOAD_IDENTIFIER' 
47100             });
47101         }
47102         
47103     
47104     Roo.each(xitems, this.addxtype, this);
47105     
47106     
47107     
47108 };
47109
47110 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47111     /**
47112      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47113      */
47114     /**
47115      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47116      */
47117     /**
47118      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47119      */
47120     buttonAlign:'center',
47121
47122     /**
47123      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47124      */
47125     minButtonWidth:75,
47126
47127     /**
47128      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47129      * This property cascades to child containers if not set.
47130      */
47131     labelAlign:'left',
47132
47133     /**
47134      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47135      * fires a looping event with that state. This is required to bind buttons to the valid
47136      * state using the config value formBind:true on the button.
47137      */
47138     monitorValid : false,
47139
47140     /**
47141      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47142      */
47143     monitorPoll : 200,
47144     
47145     /**
47146      * @cfg {String} progressUrl - Url to return progress data 
47147      */
47148     
47149     progressUrl : false,
47150   
47151     /**
47152      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47153      * fields are added and the column is closed. If no fields are passed the column remains open
47154      * until end() is called.
47155      * @param {Object} config The config to pass to the column
47156      * @param {Field} field1 (optional)
47157      * @param {Field} field2 (optional)
47158      * @param {Field} etc (optional)
47159      * @return Column The column container object
47160      */
47161     column : function(c){
47162         var col = new Roo.form.Column(c);
47163         this.start(col);
47164         if(arguments.length > 1){ // duplicate code required because of Opera
47165             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47166             this.end();
47167         }
47168         return col;
47169     },
47170
47171     /**
47172      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47173      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47174      * until end() is called.
47175      * @param {Object} config The config to pass to the fieldset
47176      * @param {Field} field1 (optional)
47177      * @param {Field} field2 (optional)
47178      * @param {Field} etc (optional)
47179      * @return FieldSet The fieldset container object
47180      */
47181     fieldset : function(c){
47182         var fs = new Roo.form.FieldSet(c);
47183         this.start(fs);
47184         if(arguments.length > 1){ // duplicate code required because of Opera
47185             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47186             this.end();
47187         }
47188         return fs;
47189     },
47190
47191     /**
47192      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47193      * fields are added and the container is closed. If no fields are passed the container remains open
47194      * until end() is called.
47195      * @param {Object} config The config to pass to the Layout
47196      * @param {Field} field1 (optional)
47197      * @param {Field} field2 (optional)
47198      * @param {Field} etc (optional)
47199      * @return Layout The container object
47200      */
47201     container : function(c){
47202         var l = new Roo.form.Layout(c);
47203         this.start(l);
47204         if(arguments.length > 1){ // duplicate code required because of Opera
47205             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47206             this.end();
47207         }
47208         return l;
47209     },
47210
47211     /**
47212      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47213      * @param {Object} container A Roo.form.Layout or subclass of Layout
47214      * @return {Form} this
47215      */
47216     start : function(c){
47217         // cascade label info
47218         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47219         this.active.stack.push(c);
47220         c.ownerCt = this.active;
47221         this.active = c;
47222         return this;
47223     },
47224
47225     /**
47226      * Closes the current open container
47227      * @return {Form} this
47228      */
47229     end : function(){
47230         if(this.active == this.root){
47231             return this;
47232         }
47233         this.active = this.active.ownerCt;
47234         return this;
47235     },
47236
47237     /**
47238      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47239      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47240      * as the label of the field.
47241      * @param {Field} field1
47242      * @param {Field} field2 (optional)
47243      * @param {Field} etc. (optional)
47244      * @return {Form} this
47245      */
47246     add : function(){
47247         this.active.stack.push.apply(this.active.stack, arguments);
47248         this.allItems.push.apply(this.allItems,arguments);
47249         var r = [];
47250         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47251             if(a[i].isFormField){
47252                 r.push(a[i]);
47253             }
47254         }
47255         if(r.length > 0){
47256             Roo.form.Form.superclass.add.apply(this, r);
47257         }
47258         return this;
47259     },
47260     
47261
47262     
47263     
47264     
47265      /**
47266      * Find any element that has been added to a form, using it's ID or name
47267      * This can include framesets, columns etc. along with regular fields..
47268      * @param {String} id - id or name to find.
47269      
47270      * @return {Element} e - or false if nothing found.
47271      */
47272     findbyId : function(id)
47273     {
47274         var ret = false;
47275         if (!id) {
47276             return ret;
47277         }
47278         Roo.each(this.allItems, function(f){
47279             if (f.id == id || f.name == id ){
47280                 ret = f;
47281                 return false;
47282             }
47283         });
47284         return ret;
47285     },
47286
47287     
47288     
47289     /**
47290      * Render this form into the passed container. This should only be called once!
47291      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47292      * @return {Form} this
47293      */
47294     render : function(ct)
47295     {
47296         
47297         
47298         
47299         ct = Roo.get(ct);
47300         var o = this.autoCreate || {
47301             tag: 'form',
47302             method : this.method || 'POST',
47303             id : this.id || Roo.id()
47304         };
47305         this.initEl(ct.createChild(o));
47306
47307         this.root.render(this.el);
47308         
47309        
47310              
47311         this.items.each(function(f){
47312             f.render('x-form-el-'+f.id);
47313         });
47314
47315         if(this.buttons.length > 0){
47316             // tables are required to maintain order and for correct IE layout
47317             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47318                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47319                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47320             }}, null, true);
47321             var tr = tb.getElementsByTagName('tr')[0];
47322             for(var i = 0, len = this.buttons.length; i < len; i++) {
47323                 var b = this.buttons[i];
47324                 var td = document.createElement('td');
47325                 td.className = 'x-form-btn-td';
47326                 b.render(tr.appendChild(td));
47327             }
47328         }
47329         if(this.monitorValid){ // initialize after render
47330             this.startMonitoring();
47331         }
47332         this.fireEvent('rendered', this);
47333         return this;
47334     },
47335
47336     /**
47337      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47338      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47339      * object or a valid Roo.DomHelper element config
47340      * @param {Function} handler The function called when the button is clicked
47341      * @param {Object} scope (optional) The scope of the handler function
47342      * @return {Roo.Button}
47343      */
47344     addButton : function(config, handler, scope){
47345         var bc = {
47346             handler: handler,
47347             scope: scope,
47348             minWidth: this.minButtonWidth,
47349             hideParent:true
47350         };
47351         if(typeof config == "string"){
47352             bc.text = config;
47353         }else{
47354             Roo.apply(bc, config);
47355         }
47356         var btn = new Roo.Button(null, bc);
47357         this.buttons.push(btn);
47358         return btn;
47359     },
47360
47361      /**
47362      * Adds a series of form elements (using the xtype property as the factory method.
47363      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47364      * @param {Object} config 
47365      */
47366     
47367     addxtype : function()
47368     {
47369         var ar = Array.prototype.slice.call(arguments, 0);
47370         var ret = false;
47371         for(var i = 0; i < ar.length; i++) {
47372             if (!ar[i]) {
47373                 continue; // skip -- if this happends something invalid got sent, we 
47374                 // should ignore it, as basically that interface element will not show up
47375                 // and that should be pretty obvious!!
47376             }
47377             
47378             if (Roo.form[ar[i].xtype]) {
47379                 ar[i].form = this;
47380                 var fe = Roo.factory(ar[i], Roo.form);
47381                 if (!ret) {
47382                     ret = fe;
47383                 }
47384                 fe.form = this;
47385                 if (fe.store) {
47386                     fe.store.form = this;
47387                 }
47388                 if (fe.isLayout) {  
47389                          
47390                     this.start(fe);
47391                     this.allItems.push(fe);
47392                     if (fe.items && fe.addxtype) {
47393                         fe.addxtype.apply(fe, fe.items);
47394                         delete fe.items;
47395                     }
47396                      this.end();
47397                     continue;
47398                 }
47399                 
47400                 
47401                  
47402                 this.add(fe);
47403               //  console.log('adding ' + ar[i].xtype);
47404             }
47405             if (ar[i].xtype == 'Button') {  
47406                 //console.log('adding button');
47407                 //console.log(ar[i]);
47408                 this.addButton(ar[i]);
47409                 this.allItems.push(fe);
47410                 continue;
47411             }
47412             
47413             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47414                 alert('end is not supported on xtype any more, use items');
47415             //    this.end();
47416             //    //console.log('adding end');
47417             }
47418             
47419         }
47420         return ret;
47421     },
47422     
47423     /**
47424      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47425      * option "monitorValid"
47426      */
47427     startMonitoring : function(){
47428         if(!this.bound){
47429             this.bound = true;
47430             Roo.TaskMgr.start({
47431                 run : this.bindHandler,
47432                 interval : this.monitorPoll || 200,
47433                 scope: this
47434             });
47435         }
47436     },
47437
47438     /**
47439      * Stops monitoring of the valid state of this form
47440      */
47441     stopMonitoring : function(){
47442         this.bound = false;
47443     },
47444
47445     // private
47446     bindHandler : function(){
47447         if(!this.bound){
47448             return false; // stops binding
47449         }
47450         var valid = true;
47451         this.items.each(function(f){
47452             if(!f.isValid(true)){
47453                 valid = false;
47454                 return false;
47455             }
47456         });
47457         for(var i = 0, len = this.buttons.length; i < len; i++){
47458             var btn = this.buttons[i];
47459             if(btn.formBind === true && btn.disabled === valid){
47460                 btn.setDisabled(!valid);
47461             }
47462         }
47463         this.fireEvent('clientvalidation', this, valid);
47464     }
47465     
47466     
47467     
47468     
47469     
47470     
47471     
47472     
47473 });
47474
47475
47476 // back compat
47477 Roo.Form = Roo.form.Form;
47478 /*
47479  * Based on:
47480  * Ext JS Library 1.1.1
47481  * Copyright(c) 2006-2007, Ext JS, LLC.
47482  *
47483  * Originally Released Under LGPL - original licence link has changed is not relivant.
47484  *
47485  * Fork - LGPL
47486  * <script type="text/javascript">
47487  */
47488
47489 // as we use this in bootstrap.
47490 Roo.namespace('Roo.form');
47491  /**
47492  * @class Roo.form.Action
47493  * Internal Class used to handle form actions
47494  * @constructor
47495  * @param {Roo.form.BasicForm} el The form element or its id
47496  * @param {Object} config Configuration options
47497  */
47498
47499  
47500  
47501 // define the action interface
47502 Roo.form.Action = function(form, options){
47503     this.form = form;
47504     this.options = options || {};
47505 };
47506 /**
47507  * Client Validation Failed
47508  * @const 
47509  */
47510 Roo.form.Action.CLIENT_INVALID = 'client';
47511 /**
47512  * Server Validation Failed
47513  * @const 
47514  */
47515 Roo.form.Action.SERVER_INVALID = 'server';
47516  /**
47517  * Connect to Server Failed
47518  * @const 
47519  */
47520 Roo.form.Action.CONNECT_FAILURE = 'connect';
47521 /**
47522  * Reading Data from Server Failed
47523  * @const 
47524  */
47525 Roo.form.Action.LOAD_FAILURE = 'load';
47526
47527 Roo.form.Action.prototype = {
47528     type : 'default',
47529     failureType : undefined,
47530     response : undefined,
47531     result : undefined,
47532
47533     // interface method
47534     run : function(options){
47535
47536     },
47537
47538     // interface method
47539     success : function(response){
47540
47541     },
47542
47543     // interface method
47544     handleResponse : function(response){
47545
47546     },
47547
47548     // default connection failure
47549     failure : function(response){
47550         
47551         this.response = response;
47552         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47553         this.form.afterAction(this, false);
47554     },
47555
47556     processResponse : function(response){
47557         this.response = response;
47558         if(!response.responseText){
47559             return true;
47560         }
47561         this.result = this.handleResponse(response);
47562         return this.result;
47563     },
47564
47565     // utility functions used internally
47566     getUrl : function(appendParams){
47567         var url = this.options.url || this.form.url || this.form.el.dom.action;
47568         if(appendParams){
47569             var p = this.getParams();
47570             if(p){
47571                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47572             }
47573         }
47574         return url;
47575     },
47576
47577     getMethod : function(){
47578         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47579     },
47580
47581     getParams : function(){
47582         var bp = this.form.baseParams;
47583         var p = this.options.params;
47584         if(p){
47585             if(typeof p == "object"){
47586                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47587             }else if(typeof p == 'string' && bp){
47588                 p += '&' + Roo.urlEncode(bp);
47589             }
47590         }else if(bp){
47591             p = Roo.urlEncode(bp);
47592         }
47593         return p;
47594     },
47595
47596     createCallback : function(){
47597         return {
47598             success: this.success,
47599             failure: this.failure,
47600             scope: this,
47601             timeout: (this.form.timeout*1000),
47602             upload: this.form.fileUpload ? this.success : undefined
47603         };
47604     }
47605 };
47606
47607 Roo.form.Action.Submit = function(form, options){
47608     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47609 };
47610
47611 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47612     type : 'submit',
47613
47614     haveProgress : false,
47615     uploadComplete : false,
47616     
47617     // uploadProgress indicator.
47618     uploadProgress : function()
47619     {
47620         if (!this.form.progressUrl) {
47621             return;
47622         }
47623         
47624         if (!this.haveProgress) {
47625             Roo.MessageBox.progress("Uploading", "Uploading");
47626         }
47627         if (this.uploadComplete) {
47628            Roo.MessageBox.hide();
47629            return;
47630         }
47631         
47632         this.haveProgress = true;
47633    
47634         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47635         
47636         var c = new Roo.data.Connection();
47637         c.request({
47638             url : this.form.progressUrl,
47639             params: {
47640                 id : uid
47641             },
47642             method: 'GET',
47643             success : function(req){
47644                //console.log(data);
47645                 var rdata = false;
47646                 var edata;
47647                 try  {
47648                    rdata = Roo.decode(req.responseText)
47649                 } catch (e) {
47650                     Roo.log("Invalid data from server..");
47651                     Roo.log(edata);
47652                     return;
47653                 }
47654                 if (!rdata || !rdata.success) {
47655                     Roo.log(rdata);
47656                     Roo.MessageBox.alert(Roo.encode(rdata));
47657                     return;
47658                 }
47659                 var data = rdata.data;
47660                 
47661                 if (this.uploadComplete) {
47662                    Roo.MessageBox.hide();
47663                    return;
47664                 }
47665                    
47666                 if (data){
47667                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47668                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47669                     );
47670                 }
47671                 this.uploadProgress.defer(2000,this);
47672             },
47673        
47674             failure: function(data) {
47675                 Roo.log('progress url failed ');
47676                 Roo.log(data);
47677             },
47678             scope : this
47679         });
47680            
47681     },
47682     
47683     
47684     run : function()
47685     {
47686         // run get Values on the form, so it syncs any secondary forms.
47687         this.form.getValues();
47688         
47689         var o = this.options;
47690         var method = this.getMethod();
47691         var isPost = method == 'POST';
47692         if(o.clientValidation === false || this.form.isValid()){
47693             
47694             if (this.form.progressUrl) {
47695                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47696                     (new Date() * 1) + '' + Math.random());
47697                     
47698             } 
47699             
47700             
47701             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47702                 form:this.form.el.dom,
47703                 url:this.getUrl(!isPost),
47704                 method: method,
47705                 params:isPost ? this.getParams() : null,
47706                 isUpload: this.form.fileUpload
47707             }));
47708             
47709             this.uploadProgress();
47710
47711         }else if (o.clientValidation !== false){ // client validation failed
47712             this.failureType = Roo.form.Action.CLIENT_INVALID;
47713             this.form.afterAction(this, false);
47714         }
47715     },
47716
47717     success : function(response)
47718     {
47719         this.uploadComplete= true;
47720         if (this.haveProgress) {
47721             Roo.MessageBox.hide();
47722         }
47723         
47724         
47725         var result = this.processResponse(response);
47726         if(result === true || result.success){
47727             this.form.afterAction(this, true);
47728             return;
47729         }
47730         if(result.errors){
47731             this.form.markInvalid(result.errors);
47732             this.failureType = Roo.form.Action.SERVER_INVALID;
47733         }
47734         this.form.afterAction(this, false);
47735     },
47736     failure : function(response)
47737     {
47738         this.uploadComplete= true;
47739         if (this.haveProgress) {
47740             Roo.MessageBox.hide();
47741         }
47742         
47743         this.response = response;
47744         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47745         this.form.afterAction(this, false);
47746     },
47747     
47748     handleResponse : function(response){
47749         if(this.form.errorReader){
47750             var rs = this.form.errorReader.read(response);
47751             var errors = [];
47752             if(rs.records){
47753                 for(var i = 0, len = rs.records.length; i < len; i++) {
47754                     var r = rs.records[i];
47755                     errors[i] = r.data;
47756                 }
47757             }
47758             if(errors.length < 1){
47759                 errors = null;
47760             }
47761             return {
47762                 success : rs.success,
47763                 errors : errors
47764             };
47765         }
47766         var ret = false;
47767         try {
47768             ret = Roo.decode(response.responseText);
47769         } catch (e) {
47770             ret = {
47771                 success: false,
47772                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47773                 errors : []
47774             };
47775         }
47776         return ret;
47777         
47778     }
47779 });
47780
47781
47782 Roo.form.Action.Load = function(form, options){
47783     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47784     this.reader = this.form.reader;
47785 };
47786
47787 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47788     type : 'load',
47789
47790     run : function(){
47791         
47792         Roo.Ajax.request(Roo.apply(
47793                 this.createCallback(), {
47794                     method:this.getMethod(),
47795                     url:this.getUrl(false),
47796                     params:this.getParams()
47797         }));
47798     },
47799
47800     success : function(response){
47801         
47802         var result = this.processResponse(response);
47803         if(result === true || !result.success || !result.data){
47804             this.failureType = Roo.form.Action.LOAD_FAILURE;
47805             this.form.afterAction(this, false);
47806             return;
47807         }
47808         this.form.clearInvalid();
47809         this.form.setValues(result.data);
47810         this.form.afterAction(this, true);
47811     },
47812
47813     handleResponse : function(response){
47814         if(this.form.reader){
47815             var rs = this.form.reader.read(response);
47816             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47817             return {
47818                 success : rs.success,
47819                 data : data
47820             };
47821         }
47822         return Roo.decode(response.responseText);
47823     }
47824 });
47825
47826 Roo.form.Action.ACTION_TYPES = {
47827     'load' : Roo.form.Action.Load,
47828     'submit' : Roo.form.Action.Submit
47829 };/*
47830  * Based on:
47831  * Ext JS Library 1.1.1
47832  * Copyright(c) 2006-2007, Ext JS, LLC.
47833  *
47834  * Originally Released Under LGPL - original licence link has changed is not relivant.
47835  *
47836  * Fork - LGPL
47837  * <script type="text/javascript">
47838  */
47839  
47840 /**
47841  * @class Roo.form.Layout
47842  * @extends Roo.Component
47843  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47844  * @constructor
47845  * @param {Object} config Configuration options
47846  */
47847 Roo.form.Layout = function(config){
47848     var xitems = [];
47849     if (config.items) {
47850         xitems = config.items;
47851         delete config.items;
47852     }
47853     Roo.form.Layout.superclass.constructor.call(this, config);
47854     this.stack = [];
47855     Roo.each(xitems, this.addxtype, this);
47856      
47857 };
47858
47859 Roo.extend(Roo.form.Layout, Roo.Component, {
47860     /**
47861      * @cfg {String/Object} autoCreate
47862      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
47863      */
47864     /**
47865      * @cfg {String/Object/Function} style
47866      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
47867      * a function which returns such a specification.
47868      */
47869     /**
47870      * @cfg {String} labelAlign
47871      * Valid values are "left," "top" and "right" (defaults to "left")
47872      */
47873     /**
47874      * @cfg {Number} labelWidth
47875      * Fixed width in pixels of all field labels (defaults to undefined)
47876      */
47877     /**
47878      * @cfg {Boolean} clear
47879      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
47880      */
47881     clear : true,
47882     /**
47883      * @cfg {String} labelSeparator
47884      * The separator to use after field labels (defaults to ':')
47885      */
47886     labelSeparator : ':',
47887     /**
47888      * @cfg {Boolean} hideLabels
47889      * True to suppress the display of field labels in this layout (defaults to false)
47890      */
47891     hideLabels : false,
47892
47893     // private
47894     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
47895     
47896     isLayout : true,
47897     
47898     // private
47899     onRender : function(ct, position){
47900         if(this.el){ // from markup
47901             this.el = Roo.get(this.el);
47902         }else {  // generate
47903             var cfg = this.getAutoCreate();
47904             this.el = ct.createChild(cfg, position);
47905         }
47906         if(this.style){
47907             this.el.applyStyles(this.style);
47908         }
47909         if(this.labelAlign){
47910             this.el.addClass('x-form-label-'+this.labelAlign);
47911         }
47912         if(this.hideLabels){
47913             this.labelStyle = "display:none";
47914             this.elementStyle = "padding-left:0;";
47915         }else{
47916             if(typeof this.labelWidth == 'number'){
47917                 this.labelStyle = "width:"+this.labelWidth+"px;";
47918                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
47919             }
47920             if(this.labelAlign == 'top'){
47921                 this.labelStyle = "width:auto;";
47922                 this.elementStyle = "padding-left:0;";
47923             }
47924         }
47925         var stack = this.stack;
47926         var slen = stack.length;
47927         if(slen > 0){
47928             if(!this.fieldTpl){
47929                 var t = new Roo.Template(
47930                     '<div class="x-form-item {5}">',
47931                         '<label for="{0}" style="{2}">{1}{4}</label>',
47932                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
47933                         '</div>',
47934                     '</div><div class="x-form-clear-left"></div>'
47935                 );
47936                 t.disableFormats = true;
47937                 t.compile();
47938                 Roo.form.Layout.prototype.fieldTpl = t;
47939             }
47940             for(var i = 0; i < slen; i++) {
47941                 if(stack[i].isFormField){
47942                     this.renderField(stack[i]);
47943                 }else{
47944                     this.renderComponent(stack[i]);
47945                 }
47946             }
47947         }
47948         if(this.clear){
47949             this.el.createChild({cls:'x-form-clear'});
47950         }
47951     },
47952
47953     // private
47954     renderField : function(f){
47955         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
47956                f.id, //0
47957                f.fieldLabel, //1
47958                f.labelStyle||this.labelStyle||'', //2
47959                this.elementStyle||'', //3
47960                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
47961                f.itemCls||this.itemCls||''  //5
47962        ], true).getPrevSibling());
47963     },
47964
47965     // private
47966     renderComponent : function(c){
47967         c.render(c.isLayout ? this.el : this.el.createChild());    
47968     },
47969     /**
47970      * Adds a object form elements (using the xtype property as the factory method.)
47971      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
47972      * @param {Object} config 
47973      */
47974     addxtype : function(o)
47975     {
47976         // create the lement.
47977         o.form = this.form;
47978         var fe = Roo.factory(o, Roo.form);
47979         this.form.allItems.push(fe);
47980         this.stack.push(fe);
47981         
47982         if (fe.isFormField) {
47983             this.form.items.add(fe);
47984         }
47985          
47986         return fe;
47987     }
47988 });
47989
47990 /**
47991  * @class Roo.form.Column
47992  * @extends Roo.form.Layout
47993  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
47994  * @constructor
47995  * @param {Object} config Configuration options
47996  */
47997 Roo.form.Column = function(config){
47998     Roo.form.Column.superclass.constructor.call(this, config);
47999 };
48000
48001 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48002     /**
48003      * @cfg {Number/String} width
48004      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48005      */
48006     /**
48007      * @cfg {String/Object} autoCreate
48008      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48009      */
48010
48011     // private
48012     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48013
48014     // private
48015     onRender : function(ct, position){
48016         Roo.form.Column.superclass.onRender.call(this, ct, position);
48017         if(this.width){
48018             this.el.setWidth(this.width);
48019         }
48020     }
48021 });
48022
48023
48024 /**
48025  * @class Roo.form.Row
48026  * @extends Roo.form.Layout
48027  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48028  * @constructor
48029  * @param {Object} config Configuration options
48030  */
48031
48032  
48033 Roo.form.Row = function(config){
48034     Roo.form.Row.superclass.constructor.call(this, config);
48035 };
48036  
48037 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48038       /**
48039      * @cfg {Number/String} width
48040      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48041      */
48042     /**
48043      * @cfg {Number/String} height
48044      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48045      */
48046     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48047     
48048     padWidth : 20,
48049     // private
48050     onRender : function(ct, position){
48051         //console.log('row render');
48052         if(!this.rowTpl){
48053             var t = new Roo.Template(
48054                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48055                     '<label for="{0}" style="{2}">{1}{4}</label>',
48056                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48057                     '</div>',
48058                 '</div>'
48059             );
48060             t.disableFormats = true;
48061             t.compile();
48062             Roo.form.Layout.prototype.rowTpl = t;
48063         }
48064         this.fieldTpl = this.rowTpl;
48065         
48066         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48067         var labelWidth = 100;
48068         
48069         if ((this.labelAlign != 'top')) {
48070             if (typeof this.labelWidth == 'number') {
48071                 labelWidth = this.labelWidth
48072             }
48073             this.padWidth =  20 + labelWidth;
48074             
48075         }
48076         
48077         Roo.form.Column.superclass.onRender.call(this, ct, position);
48078         if(this.width){
48079             this.el.setWidth(this.width);
48080         }
48081         if(this.height){
48082             this.el.setHeight(this.height);
48083         }
48084     },
48085     
48086     // private
48087     renderField : function(f){
48088         f.fieldEl = this.fieldTpl.append(this.el, [
48089                f.id, f.fieldLabel,
48090                f.labelStyle||this.labelStyle||'',
48091                this.elementStyle||'',
48092                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48093                f.itemCls||this.itemCls||'',
48094                f.width ? f.width + this.padWidth : 160 + this.padWidth
48095        ],true);
48096     }
48097 });
48098  
48099
48100 /**
48101  * @class Roo.form.FieldSet
48102  * @extends Roo.form.Layout
48103  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48104  * @constructor
48105  * @param {Object} config Configuration options
48106  */
48107 Roo.form.FieldSet = function(config){
48108     Roo.form.FieldSet.superclass.constructor.call(this, config);
48109 };
48110
48111 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48112     /**
48113      * @cfg {String} legend
48114      * The text to display as the legend for the FieldSet (defaults to '')
48115      */
48116     /**
48117      * @cfg {String/Object} autoCreate
48118      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48119      */
48120
48121     // private
48122     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48123
48124     // private
48125     onRender : function(ct, position){
48126         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48127         if(this.legend){
48128             this.setLegend(this.legend);
48129         }
48130     },
48131
48132     // private
48133     setLegend : function(text){
48134         if(this.rendered){
48135             this.el.child('legend').update(text);
48136         }
48137     }
48138 });/*
48139  * Based on:
48140  * Ext JS Library 1.1.1
48141  * Copyright(c) 2006-2007, Ext JS, LLC.
48142  *
48143  * Originally Released Under LGPL - original licence link has changed is not relivant.
48144  *
48145  * Fork - LGPL
48146  * <script type="text/javascript">
48147  */
48148 /**
48149  * @class Roo.form.VTypes
48150  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48151  * @singleton
48152  */
48153 Roo.form.VTypes = function(){
48154     // closure these in so they are only created once.
48155     var alpha = /^[a-zA-Z_]+$/;
48156     var alphanum = /^[a-zA-Z0-9_]+$/;
48157     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48158     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48159
48160     // All these messages and functions are configurable
48161     return {
48162         /**
48163          * The function used to validate email addresses
48164          * @param {String} value The email address
48165          */
48166         'email' : function(v){
48167             return email.test(v);
48168         },
48169         /**
48170          * The error text to display when the email validation function returns false
48171          * @type String
48172          */
48173         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48174         /**
48175          * The keystroke filter mask to be applied on email input
48176          * @type RegExp
48177          */
48178         'emailMask' : /[a-z0-9_\.\-@]/i,
48179
48180         /**
48181          * The function used to validate URLs
48182          * @param {String} value The URL
48183          */
48184         'url' : function(v){
48185             return url.test(v);
48186         },
48187         /**
48188          * The error text to display when the url validation function returns false
48189          * @type String
48190          */
48191         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48192         
48193         /**
48194          * The function used to validate alpha values
48195          * @param {String} value The value
48196          */
48197         'alpha' : function(v){
48198             return alpha.test(v);
48199         },
48200         /**
48201          * The error text to display when the alpha validation function returns false
48202          * @type String
48203          */
48204         'alphaText' : 'This field should only contain letters and _',
48205         /**
48206          * The keystroke filter mask to be applied on alpha input
48207          * @type RegExp
48208          */
48209         'alphaMask' : /[a-z_]/i,
48210
48211         /**
48212          * The function used to validate alphanumeric values
48213          * @param {String} value The value
48214          */
48215         'alphanum' : function(v){
48216             return alphanum.test(v);
48217         },
48218         /**
48219          * The error text to display when the alphanumeric validation function returns false
48220          * @type String
48221          */
48222         'alphanumText' : 'This field should only contain letters, numbers and _',
48223         /**
48224          * The keystroke filter mask to be applied on alphanumeric input
48225          * @type RegExp
48226          */
48227         'alphanumMask' : /[a-z0-9_]/i
48228     };
48229 }();//<script type="text/javascript">
48230
48231 /**
48232  * @class Roo.form.FCKeditor
48233  * @extends Roo.form.TextArea
48234  * Wrapper around the FCKEditor http://www.fckeditor.net
48235  * @constructor
48236  * Creates a new FCKeditor
48237  * @param {Object} config Configuration options
48238  */
48239 Roo.form.FCKeditor = function(config){
48240     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48241     this.addEvents({
48242          /**
48243          * @event editorinit
48244          * Fired when the editor is initialized - you can add extra handlers here..
48245          * @param {FCKeditor} this
48246          * @param {Object} the FCK object.
48247          */
48248         editorinit : true
48249     });
48250     
48251     
48252 };
48253 Roo.form.FCKeditor.editors = { };
48254 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48255 {
48256     //defaultAutoCreate : {
48257     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48258     //},
48259     // private
48260     /**
48261      * @cfg {Object} fck options - see fck manual for details.
48262      */
48263     fckconfig : false,
48264     
48265     /**
48266      * @cfg {Object} fck toolbar set (Basic or Default)
48267      */
48268     toolbarSet : 'Basic',
48269     /**
48270      * @cfg {Object} fck BasePath
48271      */ 
48272     basePath : '/fckeditor/',
48273     
48274     
48275     frame : false,
48276     
48277     value : '',
48278     
48279    
48280     onRender : function(ct, position)
48281     {
48282         if(!this.el){
48283             this.defaultAutoCreate = {
48284                 tag: "textarea",
48285                 style:"width:300px;height:60px;",
48286                 autocomplete: "new-password"
48287             };
48288         }
48289         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48290         /*
48291         if(this.grow){
48292             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48293             if(this.preventScrollbars){
48294                 this.el.setStyle("overflow", "hidden");
48295             }
48296             this.el.setHeight(this.growMin);
48297         }
48298         */
48299         //console.log('onrender' + this.getId() );
48300         Roo.form.FCKeditor.editors[this.getId()] = this;
48301          
48302
48303         this.replaceTextarea() ;
48304         
48305     },
48306     
48307     getEditor : function() {
48308         return this.fckEditor;
48309     },
48310     /**
48311      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48312      * @param {Mixed} value The value to set
48313      */
48314     
48315     
48316     setValue : function(value)
48317     {
48318         //console.log('setValue: ' + value);
48319         
48320         if(typeof(value) == 'undefined') { // not sure why this is happending...
48321             return;
48322         }
48323         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48324         
48325         //if(!this.el || !this.getEditor()) {
48326         //    this.value = value;
48327             //this.setValue.defer(100,this,[value]);    
48328         //    return;
48329         //} 
48330         
48331         if(!this.getEditor()) {
48332             return;
48333         }
48334         
48335         this.getEditor().SetData(value);
48336         
48337         //
48338
48339     },
48340
48341     /**
48342      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48343      * @return {Mixed} value The field value
48344      */
48345     getValue : function()
48346     {
48347         
48348         if (this.frame && this.frame.dom.style.display == 'none') {
48349             return Roo.form.FCKeditor.superclass.getValue.call(this);
48350         }
48351         
48352         if(!this.el || !this.getEditor()) {
48353            
48354            // this.getValue.defer(100,this); 
48355             return this.value;
48356         }
48357        
48358         
48359         var value=this.getEditor().GetData();
48360         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48361         return Roo.form.FCKeditor.superclass.getValue.call(this);
48362         
48363
48364     },
48365
48366     /**
48367      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48368      * @return {Mixed} value The field value
48369      */
48370     getRawValue : function()
48371     {
48372         if (this.frame && this.frame.dom.style.display == 'none') {
48373             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48374         }
48375         
48376         if(!this.el || !this.getEditor()) {
48377             //this.getRawValue.defer(100,this); 
48378             return this.value;
48379             return;
48380         }
48381         
48382         
48383         
48384         var value=this.getEditor().GetData();
48385         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48386         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48387          
48388     },
48389     
48390     setSize : function(w,h) {
48391         
48392         
48393         
48394         //if (this.frame && this.frame.dom.style.display == 'none') {
48395         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48396         //    return;
48397         //}
48398         //if(!this.el || !this.getEditor()) {
48399         //    this.setSize.defer(100,this, [w,h]); 
48400         //    return;
48401         //}
48402         
48403         
48404         
48405         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48406         
48407         this.frame.dom.setAttribute('width', w);
48408         this.frame.dom.setAttribute('height', h);
48409         this.frame.setSize(w,h);
48410         
48411     },
48412     
48413     toggleSourceEdit : function(value) {
48414         
48415       
48416          
48417         this.el.dom.style.display = value ? '' : 'none';
48418         this.frame.dom.style.display = value ?  'none' : '';
48419         
48420     },
48421     
48422     
48423     focus: function(tag)
48424     {
48425         if (this.frame.dom.style.display == 'none') {
48426             return Roo.form.FCKeditor.superclass.focus.call(this);
48427         }
48428         if(!this.el || !this.getEditor()) {
48429             this.focus.defer(100,this, [tag]); 
48430             return;
48431         }
48432         
48433         
48434         
48435         
48436         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48437         this.getEditor().Focus();
48438         if (tgs.length) {
48439             if (!this.getEditor().Selection.GetSelection()) {
48440                 this.focus.defer(100,this, [tag]); 
48441                 return;
48442             }
48443             
48444             
48445             var r = this.getEditor().EditorDocument.createRange();
48446             r.setStart(tgs[0],0);
48447             r.setEnd(tgs[0],0);
48448             this.getEditor().Selection.GetSelection().removeAllRanges();
48449             this.getEditor().Selection.GetSelection().addRange(r);
48450             this.getEditor().Focus();
48451         }
48452         
48453     },
48454     
48455     
48456     
48457     replaceTextarea : function()
48458     {
48459         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48460             return ;
48461         }
48462         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48463         //{
48464             // We must check the elements firstly using the Id and then the name.
48465         var oTextarea = document.getElementById( this.getId() );
48466         
48467         var colElementsByName = document.getElementsByName( this.getId() ) ;
48468          
48469         oTextarea.style.display = 'none' ;
48470
48471         if ( oTextarea.tabIndex ) {            
48472             this.TabIndex = oTextarea.tabIndex ;
48473         }
48474         
48475         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48476         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48477         this.frame = Roo.get(this.getId() + '___Frame')
48478     },
48479     
48480     _getConfigHtml : function()
48481     {
48482         var sConfig = '' ;
48483
48484         for ( var o in this.fckconfig ) {
48485             sConfig += sConfig.length > 0  ? '&amp;' : '';
48486             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48487         }
48488
48489         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48490     },
48491     
48492     
48493     _getIFrameHtml : function()
48494     {
48495         var sFile = 'fckeditor.html' ;
48496         /* no idea what this is about..
48497         try
48498         {
48499             if ( (/fcksource=true/i).test( window.top.location.search ) )
48500                 sFile = 'fckeditor.original.html' ;
48501         }
48502         catch (e) { 
48503         */
48504
48505         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48506         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48507         
48508         
48509         var html = '<iframe id="' + this.getId() +
48510             '___Frame" src="' + sLink +
48511             '" width="' + this.width +
48512             '" height="' + this.height + '"' +
48513             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48514             ' frameborder="0" scrolling="no"></iframe>' ;
48515
48516         return html ;
48517     },
48518     
48519     _insertHtmlBefore : function( html, element )
48520     {
48521         if ( element.insertAdjacentHTML )       {
48522             // IE
48523             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48524         } else { // Gecko
48525             var oRange = document.createRange() ;
48526             oRange.setStartBefore( element ) ;
48527             var oFragment = oRange.createContextualFragment( html );
48528             element.parentNode.insertBefore( oFragment, element ) ;
48529         }
48530     }
48531     
48532     
48533   
48534     
48535     
48536     
48537     
48538
48539 });
48540
48541 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48542
48543 function FCKeditor_OnComplete(editorInstance){
48544     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48545     f.fckEditor = editorInstance;
48546     //console.log("loaded");
48547     f.fireEvent('editorinit', f, editorInstance);
48548
48549   
48550
48551  
48552
48553
48554
48555
48556
48557
48558
48559
48560
48561
48562
48563
48564
48565
48566
48567 //<script type="text/javascript">
48568 /**
48569  * @class Roo.form.GridField
48570  * @extends Roo.form.Field
48571  * Embed a grid (or editable grid into a form)
48572  * STATUS ALPHA
48573  * 
48574  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48575  * it needs 
48576  * xgrid.store = Roo.data.Store
48577  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48578  * xgrid.store.reader = Roo.data.JsonReader 
48579  * 
48580  * 
48581  * @constructor
48582  * Creates a new GridField
48583  * @param {Object} config Configuration options
48584  */
48585 Roo.form.GridField = function(config){
48586     Roo.form.GridField.superclass.constructor.call(this, config);
48587      
48588 };
48589
48590 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48591     /**
48592      * @cfg {Number} width  - used to restrict width of grid..
48593      */
48594     width : 100,
48595     /**
48596      * @cfg {Number} height - used to restrict height of grid..
48597      */
48598     height : 50,
48599      /**
48600      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48601          * 
48602          *}
48603      */
48604     xgrid : false, 
48605     /**
48606      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48607      * {tag: "input", type: "checkbox", autocomplete: "off"})
48608      */
48609    // defaultAutoCreate : { tag: 'div' },
48610     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48611     /**
48612      * @cfg {String} addTitle Text to include for adding a title.
48613      */
48614     addTitle : false,
48615     //
48616     onResize : function(){
48617         Roo.form.Field.superclass.onResize.apply(this, arguments);
48618     },
48619
48620     initEvents : function(){
48621         // Roo.form.Checkbox.superclass.initEvents.call(this);
48622         // has no events...
48623        
48624     },
48625
48626
48627     getResizeEl : function(){
48628         return this.wrap;
48629     },
48630
48631     getPositionEl : function(){
48632         return this.wrap;
48633     },
48634
48635     // private
48636     onRender : function(ct, position){
48637         
48638         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48639         var style = this.style;
48640         delete this.style;
48641         
48642         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48643         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48644         this.viewEl = this.wrap.createChild({ tag: 'div' });
48645         if (style) {
48646             this.viewEl.applyStyles(style);
48647         }
48648         if (this.width) {
48649             this.viewEl.setWidth(this.width);
48650         }
48651         if (this.height) {
48652             this.viewEl.setHeight(this.height);
48653         }
48654         //if(this.inputValue !== undefined){
48655         //this.setValue(this.value);
48656         
48657         
48658         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48659         
48660         
48661         this.grid.render();
48662         this.grid.getDataSource().on('remove', this.refreshValue, this);
48663         this.grid.getDataSource().on('update', this.refreshValue, this);
48664         this.grid.on('afteredit', this.refreshValue, this);
48665  
48666     },
48667      
48668     
48669     /**
48670      * Sets the value of the item. 
48671      * @param {String} either an object  or a string..
48672      */
48673     setValue : function(v){
48674         //this.value = v;
48675         v = v || []; // empty set..
48676         // this does not seem smart - it really only affects memoryproxy grids..
48677         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48678             var ds = this.grid.getDataSource();
48679             // assumes a json reader..
48680             var data = {}
48681             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48682             ds.loadData( data);
48683         }
48684         // clear selection so it does not get stale.
48685         if (this.grid.sm) { 
48686             this.grid.sm.clearSelections();
48687         }
48688         
48689         Roo.form.GridField.superclass.setValue.call(this, v);
48690         this.refreshValue();
48691         // should load data in the grid really....
48692     },
48693     
48694     // private
48695     refreshValue: function() {
48696          var val = [];
48697         this.grid.getDataSource().each(function(r) {
48698             val.push(r.data);
48699         });
48700         this.el.dom.value = Roo.encode(val);
48701     }
48702     
48703      
48704     
48705     
48706 });/*
48707  * Based on:
48708  * Ext JS Library 1.1.1
48709  * Copyright(c) 2006-2007, Ext JS, LLC.
48710  *
48711  * Originally Released Under LGPL - original licence link has changed is not relivant.
48712  *
48713  * Fork - LGPL
48714  * <script type="text/javascript">
48715  */
48716 /**
48717  * @class Roo.form.DisplayField
48718  * @extends Roo.form.Field
48719  * A generic Field to display non-editable data.
48720  * @cfg {Boolean} closable (true|false) default false
48721  * @constructor
48722  * Creates a new Display Field item.
48723  * @param {Object} config Configuration options
48724  */
48725 Roo.form.DisplayField = function(config){
48726     Roo.form.DisplayField.superclass.constructor.call(this, config);
48727     
48728     this.addEvents({
48729         /**
48730          * @event close
48731          * Fires after the click the close btn
48732              * @param {Roo.form.DisplayField} this
48733              */
48734         close : true
48735     });
48736 };
48737
48738 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48739     inputType:      'hidden',
48740     allowBlank:     true,
48741     readOnly:         true,
48742     
48743  
48744     /**
48745      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48746      */
48747     focusClass : undefined,
48748     /**
48749      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48750      */
48751     fieldClass: 'x-form-field',
48752     
48753      /**
48754      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48755      */
48756     valueRenderer: undefined,
48757     
48758     width: 100,
48759     /**
48760      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48761      * {tag: "input", type: "checkbox", autocomplete: "off"})
48762      */
48763      
48764  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48765  
48766     closable : false,
48767     
48768     onResize : function(){
48769         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48770         
48771     },
48772
48773     initEvents : function(){
48774         // Roo.form.Checkbox.superclass.initEvents.call(this);
48775         // has no events...
48776         
48777         if(this.closable){
48778             this.closeEl.on('click', this.onClose, this);
48779         }
48780        
48781     },
48782
48783
48784     getResizeEl : function(){
48785         return this.wrap;
48786     },
48787
48788     getPositionEl : function(){
48789         return this.wrap;
48790     },
48791
48792     // private
48793     onRender : function(ct, position){
48794         
48795         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48796         //if(this.inputValue !== undefined){
48797         this.wrap = this.el.wrap();
48798         
48799         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48800         
48801         if(this.closable){
48802             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48803         }
48804         
48805         if (this.bodyStyle) {
48806             this.viewEl.applyStyles(this.bodyStyle);
48807         }
48808         //this.viewEl.setStyle('padding', '2px');
48809         
48810         this.setValue(this.value);
48811         
48812     },
48813 /*
48814     // private
48815     initValue : Roo.emptyFn,
48816
48817   */
48818
48819         // private
48820     onClick : function(){
48821         
48822     },
48823
48824     /**
48825      * Sets the checked state of the checkbox.
48826      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48827      */
48828     setValue : function(v){
48829         this.value = v;
48830         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48831         // this might be called before we have a dom element..
48832         if (!this.viewEl) {
48833             return;
48834         }
48835         this.viewEl.dom.innerHTML = html;
48836         Roo.form.DisplayField.superclass.setValue.call(this, v);
48837
48838     },
48839     
48840     onClose : function(e)
48841     {
48842         e.preventDefault();
48843         
48844         this.fireEvent('close', this);
48845     }
48846 });/*
48847  * 
48848  * Licence- LGPL
48849  * 
48850  */
48851
48852 /**
48853  * @class Roo.form.DayPicker
48854  * @extends Roo.form.Field
48855  * A Day picker show [M] [T] [W] ....
48856  * @constructor
48857  * Creates a new Day Picker
48858  * @param {Object} config Configuration options
48859  */
48860 Roo.form.DayPicker= function(config){
48861     Roo.form.DayPicker.superclass.constructor.call(this, config);
48862      
48863 };
48864
48865 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
48866     /**
48867      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48868      */
48869     focusClass : undefined,
48870     /**
48871      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48872      */
48873     fieldClass: "x-form-field",
48874    
48875     /**
48876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48877      * {tag: "input", type: "checkbox", autocomplete: "off"})
48878      */
48879     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
48880     
48881    
48882     actionMode : 'viewEl', 
48883     //
48884     // private
48885  
48886     inputType : 'hidden',
48887     
48888      
48889     inputElement: false, // real input element?
48890     basedOn: false, // ????
48891     
48892     isFormField: true, // not sure where this is needed!!!!
48893
48894     onResize : function(){
48895         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
48896         if(!this.boxLabel){
48897             this.el.alignTo(this.wrap, 'c-c');
48898         }
48899     },
48900
48901     initEvents : function(){
48902         Roo.form.Checkbox.superclass.initEvents.call(this);
48903         this.el.on("click", this.onClick,  this);
48904         this.el.on("change", this.onClick,  this);
48905     },
48906
48907
48908     getResizeEl : function(){
48909         return this.wrap;
48910     },
48911
48912     getPositionEl : function(){
48913         return this.wrap;
48914     },
48915
48916     
48917     // private
48918     onRender : function(ct, position){
48919         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
48920        
48921         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
48922         
48923         var r1 = '<table><tr>';
48924         var r2 = '<tr class="x-form-daypick-icons">';
48925         for (var i=0; i < 7; i++) {
48926             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
48927             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
48928         }
48929         
48930         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
48931         viewEl.select('img').on('click', this.onClick, this);
48932         this.viewEl = viewEl;   
48933         
48934         
48935         // this will not work on Chrome!!!
48936         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
48937         this.el.on('propertychange', this.setFromHidden,  this);  //ie
48938         
48939         
48940           
48941
48942     },
48943
48944     // private
48945     initValue : Roo.emptyFn,
48946
48947     /**
48948      * Returns the checked state of the checkbox.
48949      * @return {Boolean} True if checked, else false
48950      */
48951     getValue : function(){
48952         return this.el.dom.value;
48953         
48954     },
48955
48956         // private
48957     onClick : function(e){ 
48958         //this.setChecked(!this.checked);
48959         Roo.get(e.target).toggleClass('x-menu-item-checked');
48960         this.refreshValue();
48961         //if(this.el.dom.checked != this.checked){
48962         //    this.setValue(this.el.dom.checked);
48963        // }
48964     },
48965     
48966     // private
48967     refreshValue : function()
48968     {
48969         var val = '';
48970         this.viewEl.select('img',true).each(function(e,i,n)  {
48971             val += e.is(".x-menu-item-checked") ? String(n) : '';
48972         });
48973         this.setValue(val, true);
48974     },
48975
48976     /**
48977      * Sets the checked state of the checkbox.
48978      * On is always based on a string comparison between inputValue and the param.
48979      * @param {Boolean/String} value - the value to set 
48980      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
48981      */
48982     setValue : function(v,suppressEvent){
48983         if (!this.el.dom) {
48984             return;
48985         }
48986         var old = this.el.dom.value ;
48987         this.el.dom.value = v;
48988         if (suppressEvent) {
48989             return ;
48990         }
48991          
48992         // update display..
48993         this.viewEl.select('img',true).each(function(e,i,n)  {
48994             
48995             var on = e.is(".x-menu-item-checked");
48996             var newv = v.indexOf(String(n)) > -1;
48997             if (on != newv) {
48998                 e.toggleClass('x-menu-item-checked');
48999             }
49000             
49001         });
49002         
49003         
49004         this.fireEvent('change', this, v, old);
49005         
49006         
49007     },
49008    
49009     // handle setting of hidden value by some other method!!?!?
49010     setFromHidden: function()
49011     {
49012         if(!this.el){
49013             return;
49014         }
49015         //console.log("SET FROM HIDDEN");
49016         //alert('setFrom hidden');
49017         this.setValue(this.el.dom.value);
49018     },
49019     
49020     onDestroy : function()
49021     {
49022         if(this.viewEl){
49023             Roo.get(this.viewEl).remove();
49024         }
49025          
49026         Roo.form.DayPicker.superclass.onDestroy.call(this);
49027     }
49028
49029 });/*
49030  * RooJS Library 1.1.1
49031  * Copyright(c) 2008-2011  Alan Knowles
49032  *
49033  * License - LGPL
49034  */
49035  
49036
49037 /**
49038  * @class Roo.form.ComboCheck
49039  * @extends Roo.form.ComboBox
49040  * A combobox for multiple select items.
49041  *
49042  * FIXME - could do with a reset button..
49043  * 
49044  * @constructor
49045  * Create a new ComboCheck
49046  * @param {Object} config Configuration options
49047  */
49048 Roo.form.ComboCheck = function(config){
49049     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49050     // should verify some data...
49051     // like
49052     // hiddenName = required..
49053     // displayField = required
49054     // valudField == required
49055     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49056     var _t = this;
49057     Roo.each(req, function(e) {
49058         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49059             throw "Roo.form.ComboCheck : missing value for: " + e;
49060         }
49061     });
49062     
49063     
49064 };
49065
49066 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49067      
49068      
49069     editable : false,
49070      
49071     selectedClass: 'x-menu-item-checked', 
49072     
49073     // private
49074     onRender : function(ct, position){
49075         var _t = this;
49076         
49077         
49078         
49079         if(!this.tpl){
49080             var cls = 'x-combo-list';
49081
49082             
49083             this.tpl =  new Roo.Template({
49084                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49085                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49086                    '<span>{' + this.displayField + '}</span>' +
49087                     '</div>' 
49088                 
49089             });
49090         }
49091  
49092         
49093         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49094         this.view.singleSelect = false;
49095         this.view.multiSelect = true;
49096         this.view.toggleSelect = true;
49097         this.pageTb.add(new Roo.Toolbar.Fill(), {
49098             
49099             text: 'Done',
49100             handler: function()
49101             {
49102                 _t.collapse();
49103             }
49104         });
49105     },
49106     
49107     onViewOver : function(e, t){
49108         // do nothing...
49109         return;
49110         
49111     },
49112     
49113     onViewClick : function(doFocus,index){
49114         return;
49115         
49116     },
49117     select: function () {
49118         //Roo.log("SELECT CALLED");
49119     },
49120      
49121     selectByValue : function(xv, scrollIntoView){
49122         var ar = this.getValueArray();
49123         var sels = [];
49124         
49125         Roo.each(ar, function(v) {
49126             if(v === undefined || v === null){
49127                 return;
49128             }
49129             var r = this.findRecord(this.valueField, v);
49130             if(r){
49131                 sels.push(this.store.indexOf(r))
49132                 
49133             }
49134         },this);
49135         this.view.select(sels);
49136         return false;
49137     },
49138     
49139     
49140     
49141     onSelect : function(record, index){
49142        // Roo.log("onselect Called");
49143        // this is only called by the clear button now..
49144         this.view.clearSelections();
49145         this.setValue('[]');
49146         if (this.value != this.valueBefore) {
49147             this.fireEvent('change', this, this.value, this.valueBefore);
49148             this.valueBefore = this.value;
49149         }
49150     },
49151     getValueArray : function()
49152     {
49153         var ar = [] ;
49154         
49155         try {
49156             //Roo.log(this.value);
49157             if (typeof(this.value) == 'undefined') {
49158                 return [];
49159             }
49160             var ar = Roo.decode(this.value);
49161             return  ar instanceof Array ? ar : []; //?? valid?
49162             
49163         } catch(e) {
49164             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49165             return [];
49166         }
49167          
49168     },
49169     expand : function ()
49170     {
49171         
49172         Roo.form.ComboCheck.superclass.expand.call(this);
49173         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49174         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49175         
49176
49177     },
49178     
49179     collapse : function(){
49180         Roo.form.ComboCheck.superclass.collapse.call(this);
49181         var sl = this.view.getSelectedIndexes();
49182         var st = this.store;
49183         var nv = [];
49184         var tv = [];
49185         var r;
49186         Roo.each(sl, function(i) {
49187             r = st.getAt(i);
49188             nv.push(r.get(this.valueField));
49189         },this);
49190         this.setValue(Roo.encode(nv));
49191         if (this.value != this.valueBefore) {
49192
49193             this.fireEvent('change', this, this.value, this.valueBefore);
49194             this.valueBefore = this.value;
49195         }
49196         
49197     },
49198     
49199     setValue : function(v){
49200         // Roo.log(v);
49201         this.value = v;
49202         
49203         var vals = this.getValueArray();
49204         var tv = [];
49205         Roo.each(vals, function(k) {
49206             var r = this.findRecord(this.valueField, k);
49207             if(r){
49208                 tv.push(r.data[this.displayField]);
49209             }else if(this.valueNotFoundText !== undefined){
49210                 tv.push( this.valueNotFoundText );
49211             }
49212         },this);
49213        // Roo.log(tv);
49214         
49215         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49216         this.hiddenField.value = v;
49217         this.value = v;
49218     }
49219     
49220 });/*
49221  * Based on:
49222  * Ext JS Library 1.1.1
49223  * Copyright(c) 2006-2007, Ext JS, LLC.
49224  *
49225  * Originally Released Under LGPL - original licence link has changed is not relivant.
49226  *
49227  * Fork - LGPL
49228  * <script type="text/javascript">
49229  */
49230  
49231 /**
49232  * @class Roo.form.Signature
49233  * @extends Roo.form.Field
49234  * Signature field.  
49235  * @constructor
49236  * 
49237  * @param {Object} config Configuration options
49238  */
49239
49240 Roo.form.Signature = function(config){
49241     Roo.form.Signature.superclass.constructor.call(this, config);
49242     
49243     this.addEvents({// not in used??
49244          /**
49245          * @event confirm
49246          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49247              * @param {Roo.form.Signature} combo This combo box
49248              */
49249         'confirm' : true,
49250         /**
49251          * @event reset
49252          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49253              * @param {Roo.form.ComboBox} combo This combo box
49254              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49255              */
49256         'reset' : true
49257     });
49258 };
49259
49260 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49261     /**
49262      * @cfg {Object} labels Label to use when rendering a form.
49263      * defaults to 
49264      * labels : { 
49265      *      clear : "Clear",
49266      *      confirm : "Confirm"
49267      *  }
49268      */
49269     labels : { 
49270         clear : "Clear",
49271         confirm : "Confirm"
49272     },
49273     /**
49274      * @cfg {Number} width The signature panel width (defaults to 300)
49275      */
49276     width: 300,
49277     /**
49278      * @cfg {Number} height The signature panel height (defaults to 100)
49279      */
49280     height : 100,
49281     /**
49282      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49283      */
49284     allowBlank : false,
49285     
49286     //private
49287     // {Object} signPanel The signature SVG panel element (defaults to {})
49288     signPanel : {},
49289     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49290     isMouseDown : false,
49291     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49292     isConfirmed : false,
49293     // {String} signatureTmp SVG mapping string (defaults to empty string)
49294     signatureTmp : '',
49295     
49296     
49297     defaultAutoCreate : { // modified by initCompnoent..
49298         tag: "input",
49299         type:"hidden"
49300     },
49301
49302     // private
49303     onRender : function(ct, position){
49304         
49305         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49306         
49307         this.wrap = this.el.wrap({
49308             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49309         });
49310         
49311         this.createToolbar(this);
49312         this.signPanel = this.wrap.createChild({
49313                 tag: 'div',
49314                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49315             }, this.el
49316         );
49317             
49318         this.svgID = Roo.id();
49319         this.svgEl = this.signPanel.createChild({
49320               xmlns : 'http://www.w3.org/2000/svg',
49321               tag : 'svg',
49322               id : this.svgID + "-svg",
49323               width: this.width,
49324               height: this.height,
49325               viewBox: '0 0 '+this.width+' '+this.height,
49326               cn : [
49327                 {
49328                     tag: "rect",
49329                     id: this.svgID + "-svg-r",
49330                     width: this.width,
49331                     height: this.height,
49332                     fill: "#ffa"
49333                 },
49334                 {
49335                     tag: "line",
49336                     id: this.svgID + "-svg-l",
49337                     x1: "0", // start
49338                     y1: (this.height*0.8), // start set the line in 80% of height
49339                     x2: this.width, // end
49340                     y2: (this.height*0.8), // end set the line in 80% of height
49341                     'stroke': "#666",
49342                     'stroke-width': "1",
49343                     'stroke-dasharray': "3",
49344                     'shape-rendering': "crispEdges",
49345                     'pointer-events': "none"
49346                 },
49347                 {
49348                     tag: "path",
49349                     id: this.svgID + "-svg-p",
49350                     'stroke': "navy",
49351                     'stroke-width': "3",
49352                     'fill': "none",
49353                     'pointer-events': 'none'
49354                 }
49355               ]
49356         });
49357         this.createSVG();
49358         this.svgBox = this.svgEl.dom.getScreenCTM();
49359     },
49360     createSVG : function(){ 
49361         var svg = this.signPanel;
49362         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49363         var t = this;
49364
49365         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49366         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49367         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49368         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49369         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49370         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49371         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49372         
49373     },
49374     isTouchEvent : function(e){
49375         return e.type.match(/^touch/);
49376     },
49377     getCoords : function (e) {
49378         var pt    = this.svgEl.dom.createSVGPoint();
49379         pt.x = e.clientX; 
49380         pt.y = e.clientY;
49381         if (this.isTouchEvent(e)) {
49382             pt.x =  e.targetTouches[0].clientX;
49383             pt.y = e.targetTouches[0].clientY;
49384         }
49385         var a = this.svgEl.dom.getScreenCTM();
49386         var b = a.inverse();
49387         var mx = pt.matrixTransform(b);
49388         return mx.x + ',' + mx.y;
49389     },
49390     //mouse event headler 
49391     down : function (e) {
49392         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49393         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49394         
49395         this.isMouseDown = true;
49396         
49397         e.preventDefault();
49398     },
49399     move : function (e) {
49400         if (this.isMouseDown) {
49401             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49402             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49403         }
49404         
49405         e.preventDefault();
49406     },
49407     up : function (e) {
49408         this.isMouseDown = false;
49409         var sp = this.signatureTmp.split(' ');
49410         
49411         if(sp.length > 1){
49412             if(!sp[sp.length-2].match(/^L/)){
49413                 sp.pop();
49414                 sp.pop();
49415                 sp.push("");
49416                 this.signatureTmp = sp.join(" ");
49417             }
49418         }
49419         if(this.getValue() != this.signatureTmp){
49420             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49421             this.isConfirmed = false;
49422         }
49423         e.preventDefault();
49424     },
49425     
49426     /**
49427      * Protected method that will not generally be called directly. It
49428      * is called when the editor creates its toolbar. Override this method if you need to
49429      * add custom toolbar buttons.
49430      * @param {HtmlEditor} editor
49431      */
49432     createToolbar : function(editor){
49433          function btn(id, toggle, handler){
49434             var xid = fid + '-'+ id ;
49435             return {
49436                 id : xid,
49437                 cmd : id,
49438                 cls : 'x-btn-icon x-edit-'+id,
49439                 enableToggle:toggle !== false,
49440                 scope: editor, // was editor...
49441                 handler:handler||editor.relayBtnCmd,
49442                 clickEvent:'mousedown',
49443                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49444                 tabIndex:-1
49445             };
49446         }
49447         
49448         
49449         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49450         this.tb = tb;
49451         this.tb.add(
49452            {
49453                 cls : ' x-signature-btn x-signature-'+id,
49454                 scope: editor, // was editor...
49455                 handler: this.reset,
49456                 clickEvent:'mousedown',
49457                 text: this.labels.clear
49458             },
49459             {
49460                  xtype : 'Fill',
49461                  xns: Roo.Toolbar
49462             }, 
49463             {
49464                 cls : '  x-signature-btn x-signature-'+id,
49465                 scope: editor, // was editor...
49466                 handler: this.confirmHandler,
49467                 clickEvent:'mousedown',
49468                 text: this.labels.confirm
49469             }
49470         );
49471     
49472     },
49473     //public
49474     /**
49475      * when user is clicked confirm then show this image.....
49476      * 
49477      * @return {String} Image Data URI
49478      */
49479     getImageDataURI : function(){
49480         var svg = this.svgEl.dom.parentNode.innerHTML;
49481         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49482         return src; 
49483     },
49484     /**
49485      * 
49486      * @return {Boolean} this.isConfirmed
49487      */
49488     getConfirmed : function(){
49489         return this.isConfirmed;
49490     },
49491     /**
49492      * 
49493      * @return {Number} this.width
49494      */
49495     getWidth : function(){
49496         return this.width;
49497     },
49498     /**
49499      * 
49500      * @return {Number} this.height
49501      */
49502     getHeight : function(){
49503         return this.height;
49504     },
49505     // private
49506     getSignature : function(){
49507         return this.signatureTmp;
49508     },
49509     // private
49510     reset : function(){
49511         this.signatureTmp = '';
49512         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49513         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49514         this.isConfirmed = false;
49515         Roo.form.Signature.superclass.reset.call(this);
49516     },
49517     setSignature : function(s){
49518         this.signatureTmp = s;
49519         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49520         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49521         this.setValue(s);
49522         this.isConfirmed = false;
49523         Roo.form.Signature.superclass.reset.call(this);
49524     }, 
49525     test : function(){
49526 //        Roo.log(this.signPanel.dom.contentWindow.up())
49527     },
49528     //private
49529     setConfirmed : function(){
49530         
49531         
49532         
49533 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49534     },
49535     // private
49536     confirmHandler : function(){
49537         if(!this.getSignature()){
49538             return;
49539         }
49540         
49541         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49542         this.setValue(this.getSignature());
49543         this.isConfirmed = true;
49544         
49545         this.fireEvent('confirm', this);
49546     },
49547     // private
49548     // Subclasses should provide the validation implementation by overriding this
49549     validateValue : function(value){
49550         if(this.allowBlank){
49551             return true;
49552         }
49553         
49554         if(this.isConfirmed){
49555             return true;
49556         }
49557         return false;
49558     }
49559 });/*
49560  * Based on:
49561  * Ext JS Library 1.1.1
49562  * Copyright(c) 2006-2007, Ext JS, LLC.
49563  *
49564  * Originally Released Under LGPL - original licence link has changed is not relivant.
49565  *
49566  * Fork - LGPL
49567  * <script type="text/javascript">
49568  */
49569  
49570
49571 /**
49572  * @class Roo.form.ComboBox
49573  * @extends Roo.form.TriggerField
49574  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49575  * @constructor
49576  * Create a new ComboBox.
49577  * @param {Object} config Configuration options
49578  */
49579 Roo.form.Select = function(config){
49580     Roo.form.Select.superclass.constructor.call(this, config);
49581      
49582 };
49583
49584 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49585     /**
49586      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49587      */
49588     /**
49589      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49590      * rendering into an Roo.Editor, defaults to false)
49591      */
49592     /**
49593      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49594      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49595      */
49596     /**
49597      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49598      */
49599     /**
49600      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49601      * the dropdown list (defaults to undefined, with no header element)
49602      */
49603
49604      /**
49605      * @cfg {String/Roo.Template} tpl The template to use to render the output
49606      */
49607      
49608     // private
49609     defaultAutoCreate : {tag: "select"  },
49610     /**
49611      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49612      */
49613     listWidth: undefined,
49614     /**
49615      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49616      * mode = 'remote' or 'text' if mode = 'local')
49617      */
49618     displayField: undefined,
49619     /**
49620      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49621      * mode = 'remote' or 'value' if mode = 'local'). 
49622      * Note: use of a valueField requires the user make a selection
49623      * in order for a value to be mapped.
49624      */
49625     valueField: undefined,
49626     
49627     
49628     /**
49629      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49630      * field's data value (defaults to the underlying DOM element's name)
49631      */
49632     hiddenName: undefined,
49633     /**
49634      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49635      */
49636     listClass: '',
49637     /**
49638      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49639      */
49640     selectedClass: 'x-combo-selected',
49641     /**
49642      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49643      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49644      * which displays a downward arrow icon).
49645      */
49646     triggerClass : 'x-form-arrow-trigger',
49647     /**
49648      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49649      */
49650     shadow:'sides',
49651     /**
49652      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49653      * anchor positions (defaults to 'tl-bl')
49654      */
49655     listAlign: 'tl-bl?',
49656     /**
49657      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49658      */
49659     maxHeight: 300,
49660     /**
49661      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49662      * query specified by the allQuery config option (defaults to 'query')
49663      */
49664     triggerAction: 'query',
49665     /**
49666      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49667      * (defaults to 4, does not apply if editable = false)
49668      */
49669     minChars : 4,
49670     /**
49671      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49672      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49673      */
49674     typeAhead: false,
49675     /**
49676      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49677      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49678      */
49679     queryDelay: 500,
49680     /**
49681      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49682      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49683      */
49684     pageSize: 0,
49685     /**
49686      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49687      * when editable = true (defaults to false)
49688      */
49689     selectOnFocus:false,
49690     /**
49691      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49692      */
49693     queryParam: 'query',
49694     /**
49695      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49696      * when mode = 'remote' (defaults to 'Loading...')
49697      */
49698     loadingText: 'Loading...',
49699     /**
49700      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49701      */
49702     resizable: false,
49703     /**
49704      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49705      */
49706     handleHeight : 8,
49707     /**
49708      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49709      * traditional select (defaults to true)
49710      */
49711     editable: true,
49712     /**
49713      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49714      */
49715     allQuery: '',
49716     /**
49717      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49718      */
49719     mode: 'remote',
49720     /**
49721      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49722      * listWidth has a higher value)
49723      */
49724     minListWidth : 70,
49725     /**
49726      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49727      * allow the user to set arbitrary text into the field (defaults to false)
49728      */
49729     forceSelection:false,
49730     /**
49731      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49732      * if typeAhead = true (defaults to 250)
49733      */
49734     typeAheadDelay : 250,
49735     /**
49736      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49737      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49738      */
49739     valueNotFoundText : undefined,
49740     
49741     /**
49742      * @cfg {String} defaultValue The value displayed after loading the store.
49743      */
49744     defaultValue: '',
49745     
49746     /**
49747      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49748      */
49749     blockFocus : false,
49750     
49751     /**
49752      * @cfg {Boolean} disableClear Disable showing of clear button.
49753      */
49754     disableClear : false,
49755     /**
49756      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49757      */
49758     alwaysQuery : false,
49759     
49760     //private
49761     addicon : false,
49762     editicon: false,
49763     
49764     // element that contains real text value.. (when hidden is used..)
49765      
49766     // private
49767     onRender : function(ct, position){
49768         Roo.form.Field.prototype.onRender.call(this, ct, position);
49769         
49770         if(this.store){
49771             this.store.on('beforeload', this.onBeforeLoad, this);
49772             this.store.on('load', this.onLoad, this);
49773             this.store.on('loadexception', this.onLoadException, this);
49774             this.store.load({});
49775         }
49776         
49777         
49778         
49779     },
49780
49781     // private
49782     initEvents : function(){
49783         //Roo.form.ComboBox.superclass.initEvents.call(this);
49784  
49785     },
49786
49787     onDestroy : function(){
49788        
49789         if(this.store){
49790             this.store.un('beforeload', this.onBeforeLoad, this);
49791             this.store.un('load', this.onLoad, this);
49792             this.store.un('loadexception', this.onLoadException, this);
49793         }
49794         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49795     },
49796
49797     // private
49798     fireKey : function(e){
49799         if(e.isNavKeyPress() && !this.list.isVisible()){
49800             this.fireEvent("specialkey", this, e);
49801         }
49802     },
49803
49804     // private
49805     onResize: function(w, h){
49806         
49807         return; 
49808     
49809         
49810     },
49811
49812     /**
49813      * Allow or prevent the user from directly editing the field text.  If false is passed,
49814      * the user will only be able to select from the items defined in the dropdown list.  This method
49815      * is the runtime equivalent of setting the 'editable' config option at config time.
49816      * @param {Boolean} value True to allow the user to directly edit the field text
49817      */
49818     setEditable : function(value){
49819          
49820     },
49821
49822     // private
49823     onBeforeLoad : function(){
49824         
49825         Roo.log("Select before load");
49826         return;
49827     
49828         this.innerList.update(this.loadingText ?
49829                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49830         //this.restrictHeight();
49831         this.selectedIndex = -1;
49832     },
49833
49834     // private
49835     onLoad : function(){
49836
49837     
49838         var dom = this.el.dom;
49839         dom.innerHTML = '';
49840          var od = dom.ownerDocument;
49841          
49842         if (this.emptyText) {
49843             var op = od.createElement('option');
49844             op.setAttribute('value', '');
49845             op.innerHTML = String.format('{0}', this.emptyText);
49846             dom.appendChild(op);
49847         }
49848         if(this.store.getCount() > 0){
49849            
49850             var vf = this.valueField;
49851             var df = this.displayField;
49852             this.store.data.each(function(r) {
49853                 // which colmsn to use... testing - cdoe / title..
49854                 var op = od.createElement('option');
49855                 op.setAttribute('value', r.data[vf]);
49856                 op.innerHTML = String.format('{0}', r.data[df]);
49857                 dom.appendChild(op);
49858             });
49859             if (typeof(this.defaultValue != 'undefined')) {
49860                 this.setValue(this.defaultValue);
49861             }
49862             
49863              
49864         }else{
49865             //this.onEmptyResults();
49866         }
49867         //this.el.focus();
49868     },
49869     // private
49870     onLoadException : function()
49871     {
49872         dom.innerHTML = '';
49873             
49874         Roo.log("Select on load exception");
49875         return;
49876     
49877         this.collapse();
49878         Roo.log(this.store.reader.jsonData);
49879         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
49880             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
49881         }
49882         
49883         
49884     },
49885     // private
49886     onTypeAhead : function(){
49887          
49888     },
49889
49890     // private
49891     onSelect : function(record, index){
49892         Roo.log('on select?');
49893         return;
49894         if(this.fireEvent('beforeselect', this, record, index) !== false){
49895             this.setFromData(index > -1 ? record.data : false);
49896             this.collapse();
49897             this.fireEvent('select', this, record, index);
49898         }
49899     },
49900
49901     /**
49902      * Returns the currently selected field value or empty string if no value is set.
49903      * @return {String} value The selected value
49904      */
49905     getValue : function(){
49906         var dom = this.el.dom;
49907         this.value = dom.options[dom.selectedIndex].value;
49908         return this.value;
49909         
49910     },
49911
49912     /**
49913      * Clears any text/value currently set in the field
49914      */
49915     clearValue : function(){
49916         this.value = '';
49917         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
49918         
49919     },
49920
49921     /**
49922      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
49923      * will be displayed in the field.  If the value does not match the data value of an existing item,
49924      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
49925      * Otherwise the field will be blank (although the value will still be set).
49926      * @param {String} value The value to match
49927      */
49928     setValue : function(v){
49929         var d = this.el.dom;
49930         for (var i =0; i < d.options.length;i++) {
49931             if (v == d.options[i].value) {
49932                 d.selectedIndex = i;
49933                 this.value = v;
49934                 return;
49935             }
49936         }
49937         this.clearValue();
49938     },
49939     /**
49940      * @property {Object} the last set data for the element
49941      */
49942     
49943     lastData : false,
49944     /**
49945      * Sets the value of the field based on a object which is related to the record format for the store.
49946      * @param {Object} value the value to set as. or false on reset?
49947      */
49948     setFromData : function(o){
49949         Roo.log('setfrom data?');
49950          
49951         
49952         
49953     },
49954     // private
49955     reset : function(){
49956         this.clearValue();
49957     },
49958     // private
49959     findRecord : function(prop, value){
49960         
49961         return false;
49962     
49963         var record;
49964         if(this.store.getCount() > 0){
49965             this.store.each(function(r){
49966                 if(r.data[prop] == value){
49967                     record = r;
49968                     return false;
49969                 }
49970                 return true;
49971             });
49972         }
49973         return record;
49974     },
49975     
49976     getName: function()
49977     {
49978         // returns hidden if it's set..
49979         if (!this.rendered) {return ''};
49980         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
49981         
49982     },
49983      
49984
49985     
49986
49987     // private
49988     onEmptyResults : function(){
49989         Roo.log('empty results');
49990         //this.collapse();
49991     },
49992
49993     /**
49994      * Returns true if the dropdown list is expanded, else false.
49995      */
49996     isExpanded : function(){
49997         return false;
49998     },
49999
50000     /**
50001      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50002      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50003      * @param {String} value The data value of the item to select
50004      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50005      * selected item if it is not currently in view (defaults to true)
50006      * @return {Boolean} True if the value matched an item in the list, else false
50007      */
50008     selectByValue : function(v, scrollIntoView){
50009         Roo.log('select By Value');
50010         return false;
50011     
50012         if(v !== undefined && v !== null){
50013             var r = this.findRecord(this.valueField || this.displayField, v);
50014             if(r){
50015                 this.select(this.store.indexOf(r), scrollIntoView);
50016                 return true;
50017             }
50018         }
50019         return false;
50020     },
50021
50022     /**
50023      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50024      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50025      * @param {Number} index The zero-based index of the list item to select
50026      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50027      * selected item if it is not currently in view (defaults to true)
50028      */
50029     select : function(index, scrollIntoView){
50030         Roo.log('select ');
50031         return  ;
50032         
50033         this.selectedIndex = index;
50034         this.view.select(index);
50035         if(scrollIntoView !== false){
50036             var el = this.view.getNode(index);
50037             if(el){
50038                 this.innerList.scrollChildIntoView(el, false);
50039             }
50040         }
50041     },
50042
50043       
50044
50045     // private
50046     validateBlur : function(){
50047         
50048         return;
50049         
50050     },
50051
50052     // private
50053     initQuery : function(){
50054         this.doQuery(this.getRawValue());
50055     },
50056
50057     // private
50058     doForce : function(){
50059         if(this.el.dom.value.length > 0){
50060             this.el.dom.value =
50061                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50062              
50063         }
50064     },
50065
50066     /**
50067      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50068      * query allowing the query action to be canceled if needed.
50069      * @param {String} query The SQL query to execute
50070      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50071      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50072      * saved in the current store (defaults to false)
50073      */
50074     doQuery : function(q, forceAll){
50075         
50076         Roo.log('doQuery?');
50077         if(q === undefined || q === null){
50078             q = '';
50079         }
50080         var qe = {
50081             query: q,
50082             forceAll: forceAll,
50083             combo: this,
50084             cancel:false
50085         };
50086         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50087             return false;
50088         }
50089         q = qe.query;
50090         forceAll = qe.forceAll;
50091         if(forceAll === true || (q.length >= this.minChars)){
50092             if(this.lastQuery != q || this.alwaysQuery){
50093                 this.lastQuery = q;
50094                 if(this.mode == 'local'){
50095                     this.selectedIndex = -1;
50096                     if(forceAll){
50097                         this.store.clearFilter();
50098                     }else{
50099                         this.store.filter(this.displayField, q);
50100                     }
50101                     this.onLoad();
50102                 }else{
50103                     this.store.baseParams[this.queryParam] = q;
50104                     this.store.load({
50105                         params: this.getParams(q)
50106                     });
50107                     this.expand();
50108                 }
50109             }else{
50110                 this.selectedIndex = -1;
50111                 this.onLoad();   
50112             }
50113         }
50114     },
50115
50116     // private
50117     getParams : function(q){
50118         var p = {};
50119         //p[this.queryParam] = q;
50120         if(this.pageSize){
50121             p.start = 0;
50122             p.limit = this.pageSize;
50123         }
50124         return p;
50125     },
50126
50127     /**
50128      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50129      */
50130     collapse : function(){
50131         
50132     },
50133
50134     // private
50135     collapseIf : function(e){
50136         
50137     },
50138
50139     /**
50140      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50141      */
50142     expand : function(){
50143         
50144     } ,
50145
50146     // private
50147      
50148
50149     /** 
50150     * @cfg {Boolean} grow 
50151     * @hide 
50152     */
50153     /** 
50154     * @cfg {Number} growMin 
50155     * @hide 
50156     */
50157     /** 
50158     * @cfg {Number} growMax 
50159     * @hide 
50160     */
50161     /**
50162      * @hide
50163      * @method autoSize
50164      */
50165     
50166     setWidth : function()
50167     {
50168         
50169     },
50170     getResizeEl : function(){
50171         return this.el;
50172     }
50173 });//<script type="text/javasscript">
50174  
50175
50176 /**
50177  * @class Roo.DDView
50178  * A DnD enabled version of Roo.View.
50179  * @param {Element/String} container The Element in which to create the View.
50180  * @param {String} tpl The template string used to create the markup for each element of the View
50181  * @param {Object} config The configuration properties. These include all the config options of
50182  * {@link Roo.View} plus some specific to this class.<br>
50183  * <p>
50184  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50185  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50186  * <p>
50187  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50188 .x-view-drag-insert-above {
50189         border-top:1px dotted #3366cc;
50190 }
50191 .x-view-drag-insert-below {
50192         border-bottom:1px dotted #3366cc;
50193 }
50194 </code></pre>
50195  * 
50196  */
50197  
50198 Roo.DDView = function(container, tpl, config) {
50199     Roo.DDView.superclass.constructor.apply(this, arguments);
50200     this.getEl().setStyle("outline", "0px none");
50201     this.getEl().unselectable();
50202     if (this.dragGroup) {
50203                 this.setDraggable(this.dragGroup.split(","));
50204     }
50205     if (this.dropGroup) {
50206                 this.setDroppable(this.dropGroup.split(","));
50207     }
50208     if (this.deletable) {
50209         this.setDeletable();
50210     }
50211     this.isDirtyFlag = false;
50212         this.addEvents({
50213                 "drop" : true
50214         });
50215 };
50216
50217 Roo.extend(Roo.DDView, Roo.View, {
50218 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50219 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50220 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50221 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50222
50223         isFormField: true,
50224
50225         reset: Roo.emptyFn,
50226         
50227         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50228
50229         validate: function() {
50230                 return true;
50231         },
50232         
50233         destroy: function() {
50234                 this.purgeListeners();
50235                 this.getEl.removeAllListeners();
50236                 this.getEl().remove();
50237                 if (this.dragZone) {
50238                         if (this.dragZone.destroy) {
50239                                 this.dragZone.destroy();
50240                         }
50241                 }
50242                 if (this.dropZone) {
50243                         if (this.dropZone.destroy) {
50244                                 this.dropZone.destroy();
50245                         }
50246                 }
50247         },
50248
50249 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50250         getName: function() {
50251                 return this.name;
50252         },
50253
50254 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50255         setValue: function(v) {
50256                 if (!this.store) {
50257                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50258                 }
50259                 var data = {};
50260                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50261                 this.store.proxy = new Roo.data.MemoryProxy(data);
50262                 this.store.load();
50263         },
50264
50265 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50266         getValue: function() {
50267                 var result = '(';
50268                 this.store.each(function(rec) {
50269                         result += rec.id + ',';
50270                 });
50271                 return result.substr(0, result.length - 1) + ')';
50272         },
50273         
50274         getIds: function() {
50275                 var i = 0, result = new Array(this.store.getCount());
50276                 this.store.each(function(rec) {
50277                         result[i++] = rec.id;
50278                 });
50279                 return result;
50280         },
50281         
50282         isDirty: function() {
50283                 return this.isDirtyFlag;
50284         },
50285
50286 /**
50287  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50288  *      whole Element becomes the target, and this causes the drop gesture to append.
50289  */
50290     getTargetFromEvent : function(e) {
50291                 var target = e.getTarget();
50292                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50293                 target = target.parentNode;
50294                 }
50295                 if (!target) {
50296                         target = this.el.dom.lastChild || this.el.dom;
50297                 }
50298                 return target;
50299     },
50300
50301 /**
50302  *      Create the drag data which consists of an object which has the property "ddel" as
50303  *      the drag proxy element. 
50304  */
50305     getDragData : function(e) {
50306         var target = this.findItemFromChild(e.getTarget());
50307                 if(target) {
50308                         this.handleSelection(e);
50309                         var selNodes = this.getSelectedNodes();
50310             var dragData = {
50311                 source: this,
50312                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50313                 nodes: selNodes,
50314                 records: []
50315                         };
50316                         var selectedIndices = this.getSelectedIndexes();
50317                         for (var i = 0; i < selectedIndices.length; i++) {
50318                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50319                         }
50320                         if (selNodes.length == 1) {
50321                                 dragData.ddel = target.cloneNode(true); // the div element
50322                         } else {
50323                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50324                                 div.className = 'multi-proxy';
50325                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50326                                         div.appendChild(selNodes[i].cloneNode(true));
50327                                 }
50328                                 dragData.ddel = div;
50329                         }
50330             //console.log(dragData)
50331             //console.log(dragData.ddel.innerHTML)
50332                         return dragData;
50333                 }
50334         //console.log('nodragData')
50335                 return false;
50336     },
50337     
50338 /**     Specify to which ddGroup items in this DDView may be dragged. */
50339     setDraggable: function(ddGroup) {
50340         if (ddGroup instanceof Array) {
50341                 Roo.each(ddGroup, this.setDraggable, this);
50342                 return;
50343         }
50344         if (this.dragZone) {
50345                 this.dragZone.addToGroup(ddGroup);
50346         } else {
50347                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50348                                 containerScroll: true,
50349                                 ddGroup: ddGroup 
50350
50351                         });
50352 //                      Draggability implies selection. DragZone's mousedown selects the element.
50353                         if (!this.multiSelect) { this.singleSelect = true; }
50354
50355 //                      Wire the DragZone's handlers up to methods in *this*
50356                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50357                 }
50358     },
50359
50360 /**     Specify from which ddGroup this DDView accepts drops. */
50361     setDroppable: function(ddGroup) {
50362         if (ddGroup instanceof Array) {
50363                 Roo.each(ddGroup, this.setDroppable, this);
50364                 return;
50365         }
50366         if (this.dropZone) {
50367                 this.dropZone.addToGroup(ddGroup);
50368         } else {
50369                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50370                                 containerScroll: true,
50371                                 ddGroup: ddGroup
50372                         });
50373
50374 //                      Wire the DropZone's handlers up to methods in *this*
50375                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50376                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50377                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50378                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50379                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50380                 }
50381     },
50382
50383 /**     Decide whether to drop above or below a View node. */
50384     getDropPoint : function(e, n, dd){
50385         if (n == this.el.dom) { return "above"; }
50386                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50387                 var c = t + (b - t) / 2;
50388                 var y = Roo.lib.Event.getPageY(e);
50389                 if(y <= c) {
50390                         return "above";
50391                 }else{
50392                         return "below";
50393                 }
50394     },
50395
50396     onNodeEnter : function(n, dd, e, data){
50397                 return false;
50398     },
50399     
50400     onNodeOver : function(n, dd, e, data){
50401                 var pt = this.getDropPoint(e, n, dd);
50402                 // set the insert point style on the target node
50403                 var dragElClass = this.dropNotAllowed;
50404                 if (pt) {
50405                         var targetElClass;
50406                         if (pt == "above"){
50407                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50408                                 targetElClass = "x-view-drag-insert-above";
50409                         } else {
50410                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50411                                 targetElClass = "x-view-drag-insert-below";
50412                         }
50413                         if (this.lastInsertClass != targetElClass){
50414                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50415                                 this.lastInsertClass = targetElClass;
50416                         }
50417                 }
50418                 return dragElClass;
50419         },
50420
50421     onNodeOut : function(n, dd, e, data){
50422                 this.removeDropIndicators(n);
50423     },
50424
50425     onNodeDrop : function(n, dd, e, data){
50426         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50427                 return false;
50428         }
50429         var pt = this.getDropPoint(e, n, dd);
50430                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50431                 if (pt == "below") { insertAt++; }
50432                 for (var i = 0; i < data.records.length; i++) {
50433                         var r = data.records[i];
50434                         var dup = this.store.getById(r.id);
50435                         if (dup && (dd != this.dragZone)) {
50436                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50437                         } else {
50438                                 if (data.copy) {
50439                                         this.store.insert(insertAt++, r.copy());
50440                                 } else {
50441                                         data.source.isDirtyFlag = true;
50442                                         r.store.remove(r);
50443                                         this.store.insert(insertAt++, r);
50444                                 }
50445                                 this.isDirtyFlag = true;
50446                         }
50447                 }
50448                 this.dragZone.cachedTarget = null;
50449                 return true;
50450     },
50451
50452     removeDropIndicators : function(n){
50453                 if(n){
50454                         Roo.fly(n).removeClass([
50455                                 "x-view-drag-insert-above",
50456                                 "x-view-drag-insert-below"]);
50457                         this.lastInsertClass = "_noclass";
50458                 }
50459     },
50460
50461 /**
50462  *      Utility method. Add a delete option to the DDView's context menu.
50463  *      @param {String} imageUrl The URL of the "delete" icon image.
50464  */
50465         setDeletable: function(imageUrl) {
50466                 if (!this.singleSelect && !this.multiSelect) {
50467                         this.singleSelect = true;
50468                 }
50469                 var c = this.getContextMenu();
50470                 this.contextMenu.on("itemclick", function(item) {
50471                         switch (item.id) {
50472                                 case "delete":
50473                                         this.remove(this.getSelectedIndexes());
50474                                         break;
50475                         }
50476                 }, this);
50477                 this.contextMenu.add({
50478                         icon: imageUrl,
50479                         id: "delete",
50480                         text: 'Delete'
50481                 });
50482         },
50483         
50484 /**     Return the context menu for this DDView. */
50485         getContextMenu: function() {
50486                 if (!this.contextMenu) {
50487 //                      Create the View's context menu
50488                         this.contextMenu = new Roo.menu.Menu({
50489                                 id: this.id + "-contextmenu"
50490                         });
50491                         this.el.on("contextmenu", this.showContextMenu, this);
50492                 }
50493                 return this.contextMenu;
50494         },
50495         
50496         disableContextMenu: function() {
50497                 if (this.contextMenu) {
50498                         this.el.un("contextmenu", this.showContextMenu, this);
50499                 }
50500         },
50501
50502         showContextMenu: function(e, item) {
50503         item = this.findItemFromChild(e.getTarget());
50504                 if (item) {
50505                         e.stopEvent();
50506                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50507                         this.contextMenu.showAt(e.getXY());
50508             }
50509     },
50510
50511 /**
50512  *      Remove {@link Roo.data.Record}s at the specified indices.
50513  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50514  */
50515     remove: function(selectedIndices) {
50516                 selectedIndices = [].concat(selectedIndices);
50517                 for (var i = 0; i < selectedIndices.length; i++) {
50518                         var rec = this.store.getAt(selectedIndices[i]);
50519                         this.store.remove(rec);
50520                 }
50521     },
50522
50523 /**
50524  *      Double click fires the event, but also, if this is draggable, and there is only one other
50525  *      related DropZone, it transfers the selected node.
50526  */
50527     onDblClick : function(e){
50528         var item = this.findItemFromChild(e.getTarget());
50529         if(item){
50530             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50531                 return false;
50532             }
50533             if (this.dragGroup) {
50534                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50535                     while (targets.indexOf(this.dropZone) > -1) {
50536                             targets.remove(this.dropZone);
50537                                 }
50538                     if (targets.length == 1) {
50539                                         this.dragZone.cachedTarget = null;
50540                         var el = Roo.get(targets[0].getEl());
50541                         var box = el.getBox(true);
50542                         targets[0].onNodeDrop(el.dom, {
50543                                 target: el.dom,
50544                                 xy: [box.x, box.y + box.height - 1]
50545                         }, null, this.getDragData(e));
50546                     }
50547                 }
50548         }
50549     },
50550     
50551     handleSelection: function(e) {
50552                 this.dragZone.cachedTarget = null;
50553         var item = this.findItemFromChild(e.getTarget());
50554         if (!item) {
50555                 this.clearSelections(true);
50556                 return;
50557         }
50558                 if (item && (this.multiSelect || this.singleSelect)){
50559                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50560                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50561                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50562                                 this.unselect(item);
50563                         } else {
50564                                 this.select(item, this.multiSelect && e.ctrlKey);
50565                                 this.lastSelection = item;
50566                         }
50567                 }
50568     },
50569
50570     onItemClick : function(item, index, e){
50571                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50572                         return false;
50573                 }
50574                 return true;
50575     },
50576
50577     unselect : function(nodeInfo, suppressEvent){
50578                 var node = this.getNode(nodeInfo);
50579                 if(node && this.isSelected(node)){
50580                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50581                                 Roo.fly(node).removeClass(this.selectedClass);
50582                                 this.selections.remove(node);
50583                                 if(!suppressEvent){
50584                                         this.fireEvent("selectionchange", this, this.selections);
50585                                 }
50586                         }
50587                 }
50588     }
50589 });
50590 /*
50591  * Based on:
50592  * Ext JS Library 1.1.1
50593  * Copyright(c) 2006-2007, Ext JS, LLC.
50594  *
50595  * Originally Released Under LGPL - original licence link has changed is not relivant.
50596  *
50597  * Fork - LGPL
50598  * <script type="text/javascript">
50599  */
50600  
50601 /**
50602  * @class Roo.LayoutManager
50603  * @extends Roo.util.Observable
50604  * Base class for layout managers.
50605  */
50606 Roo.LayoutManager = function(container, config){
50607     Roo.LayoutManager.superclass.constructor.call(this);
50608     this.el = Roo.get(container);
50609     // ie scrollbar fix
50610     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50611         document.body.scroll = "no";
50612     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50613         this.el.position('relative');
50614     }
50615     this.id = this.el.id;
50616     this.el.addClass("x-layout-container");
50617     /** false to disable window resize monitoring @type Boolean */
50618     this.monitorWindowResize = true;
50619     this.regions = {};
50620     this.addEvents({
50621         /**
50622          * @event layout
50623          * Fires when a layout is performed. 
50624          * @param {Roo.LayoutManager} this
50625          */
50626         "layout" : true,
50627         /**
50628          * @event regionresized
50629          * Fires when the user resizes a region. 
50630          * @param {Roo.LayoutRegion} region The resized region
50631          * @param {Number} newSize The new size (width for east/west, height for north/south)
50632          */
50633         "regionresized" : true,
50634         /**
50635          * @event regioncollapsed
50636          * Fires when a region is collapsed. 
50637          * @param {Roo.LayoutRegion} region The collapsed region
50638          */
50639         "regioncollapsed" : true,
50640         /**
50641          * @event regionexpanded
50642          * Fires when a region is expanded.  
50643          * @param {Roo.LayoutRegion} region The expanded region
50644          */
50645         "regionexpanded" : true
50646     });
50647     this.updating = false;
50648     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50649 };
50650
50651 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50652     /**
50653      * Returns true if this layout is currently being updated
50654      * @return {Boolean}
50655      */
50656     isUpdating : function(){
50657         return this.updating; 
50658     },
50659     
50660     /**
50661      * Suspend the LayoutManager from doing auto-layouts while
50662      * making multiple add or remove calls
50663      */
50664     beginUpdate : function(){
50665         this.updating = true;    
50666     },
50667     
50668     /**
50669      * Restore auto-layouts and optionally disable the manager from performing a layout
50670      * @param {Boolean} noLayout true to disable a layout update 
50671      */
50672     endUpdate : function(noLayout){
50673         this.updating = false;
50674         if(!noLayout){
50675             this.layout();
50676         }    
50677     },
50678     
50679     layout: function(){
50680         
50681     },
50682     
50683     onRegionResized : function(region, newSize){
50684         this.fireEvent("regionresized", region, newSize);
50685         this.layout();
50686     },
50687     
50688     onRegionCollapsed : function(region){
50689         this.fireEvent("regioncollapsed", region);
50690     },
50691     
50692     onRegionExpanded : function(region){
50693         this.fireEvent("regionexpanded", region);
50694     },
50695         
50696     /**
50697      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50698      * performs box-model adjustments.
50699      * @return {Object} The size as an object {width: (the width), height: (the height)}
50700      */
50701     getViewSize : function(){
50702         var size;
50703         if(this.el.dom != document.body){
50704             size = this.el.getSize();
50705         }else{
50706             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50707         }
50708         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50709         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50710         return size;
50711     },
50712     
50713     /**
50714      * Returns the Element this layout is bound to.
50715      * @return {Roo.Element}
50716      */
50717     getEl : function(){
50718         return this.el;
50719     },
50720     
50721     /**
50722      * Returns the specified region.
50723      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50724      * @return {Roo.LayoutRegion}
50725      */
50726     getRegion : function(target){
50727         return this.regions[target.toLowerCase()];
50728     },
50729     
50730     onWindowResize : function(){
50731         if(this.monitorWindowResize){
50732             this.layout();
50733         }
50734     }
50735 });/*
50736  * Based on:
50737  * Ext JS Library 1.1.1
50738  * Copyright(c) 2006-2007, Ext JS, LLC.
50739  *
50740  * Originally Released Under LGPL - original licence link has changed is not relivant.
50741  *
50742  * Fork - LGPL
50743  * <script type="text/javascript">
50744  */
50745 /**
50746  * @class Roo.BorderLayout
50747  * @extends Roo.LayoutManager
50748  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50749  * please see: <br><br>
50750  * <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>
50751  * <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>
50752  * Example:
50753  <pre><code>
50754  var layout = new Roo.BorderLayout(document.body, {
50755     north: {
50756         initialSize: 25,
50757         titlebar: false
50758     },
50759     west: {
50760         split:true,
50761         initialSize: 200,
50762         minSize: 175,
50763         maxSize: 400,
50764         titlebar: true,
50765         collapsible: true
50766     },
50767     east: {
50768         split:true,
50769         initialSize: 202,
50770         minSize: 175,
50771         maxSize: 400,
50772         titlebar: true,
50773         collapsible: true
50774     },
50775     south: {
50776         split:true,
50777         initialSize: 100,
50778         minSize: 100,
50779         maxSize: 200,
50780         titlebar: true,
50781         collapsible: true
50782     },
50783     center: {
50784         titlebar: true,
50785         autoScroll:true,
50786         resizeTabs: true,
50787         minTabWidth: 50,
50788         preferredTabWidth: 150
50789     }
50790 });
50791
50792 // shorthand
50793 var CP = Roo.ContentPanel;
50794
50795 layout.beginUpdate();
50796 layout.add("north", new CP("north", "North"));
50797 layout.add("south", new CP("south", {title: "South", closable: true}));
50798 layout.add("west", new CP("west", {title: "West"}));
50799 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50800 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50801 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50802 layout.getRegion("center").showPanel("center1");
50803 layout.endUpdate();
50804 </code></pre>
50805
50806 <b>The container the layout is rendered into can be either the body element or any other element.
50807 If it is not the body element, the container needs to either be an absolute positioned element,
50808 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50809 the container size if it is not the body element.</b>
50810
50811 * @constructor
50812 * Create a new BorderLayout
50813 * @param {String/HTMLElement/Element} container The container this layout is bound to
50814 * @param {Object} config Configuration options
50815  */
50816 Roo.BorderLayout = function(container, config){
50817     config = config || {};
50818     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50819     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50820     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50821         var target = this.factory.validRegions[i];
50822         if(config[target]){
50823             this.addRegion(target, config[target]);
50824         }
50825     }
50826 };
50827
50828 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50829     /**
50830      * Creates and adds a new region if it doesn't already exist.
50831      * @param {String} target The target region key (north, south, east, west or center).
50832      * @param {Object} config The regions config object
50833      * @return {BorderLayoutRegion} The new region
50834      */
50835     addRegion : function(target, config){
50836         if(!this.regions[target]){
50837             var r = this.factory.create(target, this, config);
50838             this.bindRegion(target, r);
50839         }
50840         return this.regions[target];
50841     },
50842
50843     // private (kinda)
50844     bindRegion : function(name, r){
50845         this.regions[name] = r;
50846         r.on("visibilitychange", this.layout, this);
50847         r.on("paneladded", this.layout, this);
50848         r.on("panelremoved", this.layout, this);
50849         r.on("invalidated", this.layout, this);
50850         r.on("resized", this.onRegionResized, this);
50851         r.on("collapsed", this.onRegionCollapsed, this);
50852         r.on("expanded", this.onRegionExpanded, this);
50853     },
50854
50855     /**
50856      * Performs a layout update.
50857      */
50858     layout : function(){
50859         if(this.updating) {
50860             return;
50861         }
50862         var size = this.getViewSize();
50863         var w = size.width;
50864         var h = size.height;
50865         var centerW = w;
50866         var centerH = h;
50867         var centerY = 0;
50868         var centerX = 0;
50869         //var x = 0, y = 0;
50870
50871         var rs = this.regions;
50872         var north = rs["north"];
50873         var south = rs["south"]; 
50874         var west = rs["west"];
50875         var east = rs["east"];
50876         var center = rs["center"];
50877         //if(this.hideOnLayout){ // not supported anymore
50878             //c.el.setStyle("display", "none");
50879         //}
50880         if(north && north.isVisible()){
50881             var b = north.getBox();
50882             var m = north.getMargins();
50883             b.width = w - (m.left+m.right);
50884             b.x = m.left;
50885             b.y = m.top;
50886             centerY = b.height + b.y + m.bottom;
50887             centerH -= centerY;
50888             north.updateBox(this.safeBox(b));
50889         }
50890         if(south && south.isVisible()){
50891             var b = south.getBox();
50892             var m = south.getMargins();
50893             b.width = w - (m.left+m.right);
50894             b.x = m.left;
50895             var totalHeight = (b.height + m.top + m.bottom);
50896             b.y = h - totalHeight + m.top;
50897             centerH -= totalHeight;
50898             south.updateBox(this.safeBox(b));
50899         }
50900         if(west && west.isVisible()){
50901             var b = west.getBox();
50902             var m = west.getMargins();
50903             b.height = centerH - (m.top+m.bottom);
50904             b.x = m.left;
50905             b.y = centerY + m.top;
50906             var totalWidth = (b.width + m.left + m.right);
50907             centerX += totalWidth;
50908             centerW -= totalWidth;
50909             west.updateBox(this.safeBox(b));
50910         }
50911         if(east && east.isVisible()){
50912             var b = east.getBox();
50913             var m = east.getMargins();
50914             b.height = centerH - (m.top+m.bottom);
50915             var totalWidth = (b.width + m.left + m.right);
50916             b.x = w - totalWidth + m.left;
50917             b.y = centerY + m.top;
50918             centerW -= totalWidth;
50919             east.updateBox(this.safeBox(b));
50920         }
50921         if(center){
50922             var m = center.getMargins();
50923             var centerBox = {
50924                 x: centerX + m.left,
50925                 y: centerY + m.top,
50926                 width: centerW - (m.left+m.right),
50927                 height: centerH - (m.top+m.bottom)
50928             };
50929             //if(this.hideOnLayout){
50930                 //center.el.setStyle("display", "block");
50931             //}
50932             center.updateBox(this.safeBox(centerBox));
50933         }
50934         this.el.repaint();
50935         this.fireEvent("layout", this);
50936     },
50937
50938     // private
50939     safeBox : function(box){
50940         box.width = Math.max(0, box.width);
50941         box.height = Math.max(0, box.height);
50942         return box;
50943     },
50944
50945     /**
50946      * Adds a ContentPanel (or subclass) to this layout.
50947      * @param {String} target The target region key (north, south, east, west or center).
50948      * @param {Roo.ContentPanel} panel The panel to add
50949      * @return {Roo.ContentPanel} The added panel
50950      */
50951     add : function(target, panel){
50952          
50953         target = target.toLowerCase();
50954         return this.regions[target].add(panel);
50955     },
50956
50957     /**
50958      * Remove a ContentPanel (or subclass) to this layout.
50959      * @param {String} target The target region key (north, south, east, west or center).
50960      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
50961      * @return {Roo.ContentPanel} The removed panel
50962      */
50963     remove : function(target, panel){
50964         target = target.toLowerCase();
50965         return this.regions[target].remove(panel);
50966     },
50967
50968     /**
50969      * Searches all regions for a panel with the specified id
50970      * @param {String} panelId
50971      * @return {Roo.ContentPanel} The panel or null if it wasn't found
50972      */
50973     findPanel : function(panelId){
50974         var rs = this.regions;
50975         for(var target in rs){
50976             if(typeof rs[target] != "function"){
50977                 var p = rs[target].getPanel(panelId);
50978                 if(p){
50979                     return p;
50980                 }
50981             }
50982         }
50983         return null;
50984     },
50985
50986     /**
50987      * Searches all regions for a panel with the specified id and activates (shows) it.
50988      * @param {String/ContentPanel} panelId The panels id or the panel itself
50989      * @return {Roo.ContentPanel} The shown panel or null
50990      */
50991     showPanel : function(panelId) {
50992       var rs = this.regions;
50993       for(var target in rs){
50994          var r = rs[target];
50995          if(typeof r != "function"){
50996             if(r.hasPanel(panelId)){
50997                return r.showPanel(panelId);
50998             }
50999          }
51000       }
51001       return null;
51002    },
51003
51004    /**
51005      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51006      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51007      */
51008     restoreState : function(provider){
51009         if(!provider){
51010             provider = Roo.state.Manager;
51011         }
51012         var sm = new Roo.LayoutStateManager();
51013         sm.init(this, provider);
51014     },
51015
51016     /**
51017      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51018      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51019      * a valid ContentPanel config object.  Example:
51020      * <pre><code>
51021 // Create the main layout
51022 var layout = new Roo.BorderLayout('main-ct', {
51023     west: {
51024         split:true,
51025         minSize: 175,
51026         titlebar: true
51027     },
51028     center: {
51029         title:'Components'
51030     }
51031 }, 'main-ct');
51032
51033 // Create and add multiple ContentPanels at once via configs
51034 layout.batchAdd({
51035    west: {
51036        id: 'source-files',
51037        autoCreate:true,
51038        title:'Ext Source Files',
51039        autoScroll:true,
51040        fitToFrame:true
51041    },
51042    center : {
51043        el: cview,
51044        autoScroll:true,
51045        fitToFrame:true,
51046        toolbar: tb,
51047        resizeEl:'cbody'
51048    }
51049 });
51050 </code></pre>
51051      * @param {Object} regions An object containing ContentPanel configs by region name
51052      */
51053     batchAdd : function(regions){
51054         this.beginUpdate();
51055         for(var rname in regions){
51056             var lr = this.regions[rname];
51057             if(lr){
51058                 this.addTypedPanels(lr, regions[rname]);
51059             }
51060         }
51061         this.endUpdate();
51062     },
51063
51064     // private
51065     addTypedPanels : function(lr, ps){
51066         if(typeof ps == 'string'){
51067             lr.add(new Roo.ContentPanel(ps));
51068         }
51069         else if(ps instanceof Array){
51070             for(var i =0, len = ps.length; i < len; i++){
51071                 this.addTypedPanels(lr, ps[i]);
51072             }
51073         }
51074         else if(!ps.events){ // raw config?
51075             var el = ps.el;
51076             delete ps.el; // prevent conflict
51077             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51078         }
51079         else {  // panel object assumed!
51080             lr.add(ps);
51081         }
51082     },
51083     /**
51084      * Adds a xtype elements to the layout.
51085      * <pre><code>
51086
51087 layout.addxtype({
51088        xtype : 'ContentPanel',
51089        region: 'west',
51090        items: [ .... ]
51091    }
51092 );
51093
51094 layout.addxtype({
51095         xtype : 'NestedLayoutPanel',
51096         region: 'west',
51097         layout: {
51098            center: { },
51099            west: { }   
51100         },
51101         items : [ ... list of content panels or nested layout panels.. ]
51102    }
51103 );
51104 </code></pre>
51105      * @param {Object} cfg Xtype definition of item to add.
51106      */
51107     addxtype : function(cfg)
51108     {
51109         // basically accepts a pannel...
51110         // can accept a layout region..!?!?
51111         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51112         
51113         if (!cfg.xtype.match(/Panel$/)) {
51114             return false;
51115         }
51116         var ret = false;
51117         
51118         if (typeof(cfg.region) == 'undefined') {
51119             Roo.log("Failed to add Panel, region was not set");
51120             Roo.log(cfg);
51121             return false;
51122         }
51123         var region = cfg.region;
51124         delete cfg.region;
51125         
51126           
51127         var xitems = [];
51128         if (cfg.items) {
51129             xitems = cfg.items;
51130             delete cfg.items;
51131         }
51132         var nb = false;
51133         
51134         switch(cfg.xtype) 
51135         {
51136             case 'ContentPanel':  // ContentPanel (el, cfg)
51137             case 'ScrollPanel':  // ContentPanel (el, cfg)
51138             case 'ViewPanel': 
51139                 if(cfg.autoCreate) {
51140                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51141                 } else {
51142                     var el = this.el.createChild();
51143                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51144                 }
51145                 
51146                 this.add(region, ret);
51147                 break;
51148             
51149             
51150             case 'TreePanel': // our new panel!
51151                 cfg.el = this.el.createChild();
51152                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51153                 this.add(region, ret);
51154                 break;
51155             
51156             case 'NestedLayoutPanel': 
51157                 // create a new Layout (which is  a Border Layout...
51158                 var el = this.el.createChild();
51159                 var clayout = cfg.layout;
51160                 delete cfg.layout;
51161                 clayout.items   = clayout.items  || [];
51162                 // replace this exitems with the clayout ones..
51163                 xitems = clayout.items;
51164                  
51165                 
51166                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51167                     cfg.background = false;
51168                 }
51169                 var layout = new Roo.BorderLayout(el, clayout);
51170                 
51171                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51172                 //console.log('adding nested layout panel '  + cfg.toSource());
51173                 this.add(region, ret);
51174                 nb = {}; /// find first...
51175                 break;
51176                 
51177             case 'GridPanel': 
51178             
51179                 // needs grid and region
51180                 
51181                 //var el = this.getRegion(region).el.createChild();
51182                 var el = this.el.createChild();
51183                 // create the grid first...
51184                 
51185                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51186                 delete cfg.grid;
51187                 if (region == 'center' && this.active ) {
51188                     cfg.background = false;
51189                 }
51190                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51191                 
51192                 this.add(region, ret);
51193                 if (cfg.background) {
51194                     ret.on('activate', function(gp) {
51195                         if (!gp.grid.rendered) {
51196                             gp.grid.render();
51197                         }
51198                     });
51199                 } else {
51200                     grid.render();
51201                 }
51202                 break;
51203            
51204            
51205            
51206                 
51207                 
51208                 
51209             default:
51210                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51211                     
51212                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51213                     this.add(region, ret);
51214                 } else {
51215                 
51216                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51217                     return null;
51218                 }
51219                 
51220              // GridPanel (grid, cfg)
51221             
51222         }
51223         this.beginUpdate();
51224         // add children..
51225         var region = '';
51226         var abn = {};
51227         Roo.each(xitems, function(i)  {
51228             region = nb && i.region ? i.region : false;
51229             
51230             var add = ret.addxtype(i);
51231            
51232             if (region) {
51233                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51234                 if (!i.background) {
51235                     abn[region] = nb[region] ;
51236                 }
51237             }
51238             
51239         });
51240         this.endUpdate();
51241
51242         // make the last non-background panel active..
51243         //if (nb) { Roo.log(abn); }
51244         if (nb) {
51245             
51246             for(var r in abn) {
51247                 region = this.getRegion(r);
51248                 if (region) {
51249                     // tried using nb[r], but it does not work..
51250                      
51251                     region.showPanel(abn[r]);
51252                    
51253                 }
51254             }
51255         }
51256         return ret;
51257         
51258     }
51259 });
51260
51261 /**
51262  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51263  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51264  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51265  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51266  * <pre><code>
51267 // shorthand
51268 var CP = Roo.ContentPanel;
51269
51270 var layout = Roo.BorderLayout.create({
51271     north: {
51272         initialSize: 25,
51273         titlebar: false,
51274         panels: [new CP("north", "North")]
51275     },
51276     west: {
51277         split:true,
51278         initialSize: 200,
51279         minSize: 175,
51280         maxSize: 400,
51281         titlebar: true,
51282         collapsible: true,
51283         panels: [new CP("west", {title: "West"})]
51284     },
51285     east: {
51286         split:true,
51287         initialSize: 202,
51288         minSize: 175,
51289         maxSize: 400,
51290         titlebar: true,
51291         collapsible: true,
51292         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51293     },
51294     south: {
51295         split:true,
51296         initialSize: 100,
51297         minSize: 100,
51298         maxSize: 200,
51299         titlebar: true,
51300         collapsible: true,
51301         panels: [new CP("south", {title: "South", closable: true})]
51302     },
51303     center: {
51304         titlebar: true,
51305         autoScroll:true,
51306         resizeTabs: true,
51307         minTabWidth: 50,
51308         preferredTabWidth: 150,
51309         panels: [
51310             new CP("center1", {title: "Close Me", closable: true}),
51311             new CP("center2", {title: "Center Panel", closable: false})
51312         ]
51313     }
51314 }, document.body);
51315
51316 layout.getRegion("center").showPanel("center1");
51317 </code></pre>
51318  * @param config
51319  * @param targetEl
51320  */
51321 Roo.BorderLayout.create = function(config, targetEl){
51322     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51323     layout.beginUpdate();
51324     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51325     for(var j = 0, jlen = regions.length; j < jlen; j++){
51326         var lr = regions[j];
51327         if(layout.regions[lr] && config[lr].panels){
51328             var r = layout.regions[lr];
51329             var ps = config[lr].panels;
51330             layout.addTypedPanels(r, ps);
51331         }
51332     }
51333     layout.endUpdate();
51334     return layout;
51335 };
51336
51337 // private
51338 Roo.BorderLayout.RegionFactory = {
51339     // private
51340     validRegions : ["north","south","east","west","center"],
51341
51342     // private
51343     create : function(target, mgr, config){
51344         target = target.toLowerCase();
51345         if(config.lightweight || config.basic){
51346             return new Roo.BasicLayoutRegion(mgr, config, target);
51347         }
51348         switch(target){
51349             case "north":
51350                 return new Roo.NorthLayoutRegion(mgr, config);
51351             case "south":
51352                 return new Roo.SouthLayoutRegion(mgr, config);
51353             case "east":
51354                 return new Roo.EastLayoutRegion(mgr, config);
51355             case "west":
51356                 return new Roo.WestLayoutRegion(mgr, config);
51357             case "center":
51358                 return new Roo.CenterLayoutRegion(mgr, config);
51359         }
51360         throw 'Layout region "'+target+'" not supported.';
51361     }
51362 };/*
51363  * Based on:
51364  * Ext JS Library 1.1.1
51365  * Copyright(c) 2006-2007, Ext JS, LLC.
51366  *
51367  * Originally Released Under LGPL - original licence link has changed is not relivant.
51368  *
51369  * Fork - LGPL
51370  * <script type="text/javascript">
51371  */
51372  
51373 /**
51374  * @class Roo.BasicLayoutRegion
51375  * @extends Roo.util.Observable
51376  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51377  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51378  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51379  */
51380 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51381     this.mgr = mgr;
51382     this.position  = pos;
51383     this.events = {
51384         /**
51385          * @scope Roo.BasicLayoutRegion
51386          */
51387         
51388         /**
51389          * @event beforeremove
51390          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51391          * @param {Roo.LayoutRegion} this
51392          * @param {Roo.ContentPanel} panel The panel
51393          * @param {Object} e The cancel event object
51394          */
51395         "beforeremove" : true,
51396         /**
51397          * @event invalidated
51398          * Fires when the layout for this region is changed.
51399          * @param {Roo.LayoutRegion} this
51400          */
51401         "invalidated" : true,
51402         /**
51403          * @event visibilitychange
51404          * Fires when this region is shown or hidden 
51405          * @param {Roo.LayoutRegion} this
51406          * @param {Boolean} visibility true or false
51407          */
51408         "visibilitychange" : true,
51409         /**
51410          * @event paneladded
51411          * Fires when a panel is added. 
51412          * @param {Roo.LayoutRegion} this
51413          * @param {Roo.ContentPanel} panel The panel
51414          */
51415         "paneladded" : true,
51416         /**
51417          * @event panelremoved
51418          * Fires when a panel is removed. 
51419          * @param {Roo.LayoutRegion} this
51420          * @param {Roo.ContentPanel} panel The panel
51421          */
51422         "panelremoved" : true,
51423         /**
51424          * @event collapsed
51425          * Fires when this region is collapsed.
51426          * @param {Roo.LayoutRegion} this
51427          */
51428         "collapsed" : true,
51429         /**
51430          * @event expanded
51431          * Fires when this region is expanded.
51432          * @param {Roo.LayoutRegion} this
51433          */
51434         "expanded" : true,
51435         /**
51436          * @event slideshow
51437          * Fires when this region is slid into view.
51438          * @param {Roo.LayoutRegion} this
51439          */
51440         "slideshow" : true,
51441         /**
51442          * @event slidehide
51443          * Fires when this region slides out of view. 
51444          * @param {Roo.LayoutRegion} this
51445          */
51446         "slidehide" : true,
51447         /**
51448          * @event panelactivated
51449          * Fires when a panel is activated. 
51450          * @param {Roo.LayoutRegion} this
51451          * @param {Roo.ContentPanel} panel The activated panel
51452          */
51453         "panelactivated" : true,
51454         /**
51455          * @event resized
51456          * Fires when the user resizes this region. 
51457          * @param {Roo.LayoutRegion} this
51458          * @param {Number} newSize The new size (width for east/west, height for north/south)
51459          */
51460         "resized" : true
51461     };
51462     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51463     this.panels = new Roo.util.MixedCollection();
51464     this.panels.getKey = this.getPanelId.createDelegate(this);
51465     this.box = null;
51466     this.activePanel = null;
51467     // ensure listeners are added...
51468     
51469     if (config.listeners || config.events) {
51470         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51471             listeners : config.listeners || {},
51472             events : config.events || {}
51473         });
51474     }
51475     
51476     if(skipConfig !== true){
51477         this.applyConfig(config);
51478     }
51479 };
51480
51481 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51482     getPanelId : function(p){
51483         return p.getId();
51484     },
51485     
51486     applyConfig : function(config){
51487         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51488         this.config = config;
51489         
51490     },
51491     
51492     /**
51493      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51494      * the width, for horizontal (north, south) the height.
51495      * @param {Number} newSize The new width or height
51496      */
51497     resizeTo : function(newSize){
51498         var el = this.el ? this.el :
51499                  (this.activePanel ? this.activePanel.getEl() : null);
51500         if(el){
51501             switch(this.position){
51502                 case "east":
51503                 case "west":
51504                     el.setWidth(newSize);
51505                     this.fireEvent("resized", this, newSize);
51506                 break;
51507                 case "north":
51508                 case "south":
51509                     el.setHeight(newSize);
51510                     this.fireEvent("resized", this, newSize);
51511                 break;                
51512             }
51513         }
51514     },
51515     
51516     getBox : function(){
51517         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51518     },
51519     
51520     getMargins : function(){
51521         return this.margins;
51522     },
51523     
51524     updateBox : function(box){
51525         this.box = box;
51526         var el = this.activePanel.getEl();
51527         el.dom.style.left = box.x + "px";
51528         el.dom.style.top = box.y + "px";
51529         this.activePanel.setSize(box.width, box.height);
51530     },
51531     
51532     /**
51533      * Returns the container element for this region.
51534      * @return {Roo.Element}
51535      */
51536     getEl : function(){
51537         return this.activePanel;
51538     },
51539     
51540     /**
51541      * Returns true if this region is currently visible.
51542      * @return {Boolean}
51543      */
51544     isVisible : function(){
51545         return this.activePanel ? true : false;
51546     },
51547     
51548     setActivePanel : function(panel){
51549         panel = this.getPanel(panel);
51550         if(this.activePanel && this.activePanel != panel){
51551             this.activePanel.setActiveState(false);
51552             this.activePanel.getEl().setLeftTop(-10000,-10000);
51553         }
51554         this.activePanel = panel;
51555         panel.setActiveState(true);
51556         if(this.box){
51557             panel.setSize(this.box.width, this.box.height);
51558         }
51559         this.fireEvent("panelactivated", this, panel);
51560         this.fireEvent("invalidated");
51561     },
51562     
51563     /**
51564      * Show the specified panel.
51565      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51566      * @return {Roo.ContentPanel} The shown panel or null
51567      */
51568     showPanel : function(panel){
51569         if(panel = this.getPanel(panel)){
51570             this.setActivePanel(panel);
51571         }
51572         return panel;
51573     },
51574     
51575     /**
51576      * Get the active panel for this region.
51577      * @return {Roo.ContentPanel} The active panel or null
51578      */
51579     getActivePanel : function(){
51580         return this.activePanel;
51581     },
51582     
51583     /**
51584      * Add the passed ContentPanel(s)
51585      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51586      * @return {Roo.ContentPanel} The panel added (if only one was added)
51587      */
51588     add : function(panel){
51589         if(arguments.length > 1){
51590             for(var i = 0, len = arguments.length; i < len; i++) {
51591                 this.add(arguments[i]);
51592             }
51593             return null;
51594         }
51595         if(this.hasPanel(panel)){
51596             this.showPanel(panel);
51597             return panel;
51598         }
51599         var el = panel.getEl();
51600         if(el.dom.parentNode != this.mgr.el.dom){
51601             this.mgr.el.dom.appendChild(el.dom);
51602         }
51603         if(panel.setRegion){
51604             panel.setRegion(this);
51605         }
51606         this.panels.add(panel);
51607         el.setStyle("position", "absolute");
51608         if(!panel.background){
51609             this.setActivePanel(panel);
51610             if(this.config.initialSize && this.panels.getCount()==1){
51611                 this.resizeTo(this.config.initialSize);
51612             }
51613         }
51614         this.fireEvent("paneladded", this, panel);
51615         return panel;
51616     },
51617     
51618     /**
51619      * Returns true if the panel is in this region.
51620      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51621      * @return {Boolean}
51622      */
51623     hasPanel : function(panel){
51624         if(typeof panel == "object"){ // must be panel obj
51625             panel = panel.getId();
51626         }
51627         return this.getPanel(panel) ? true : false;
51628     },
51629     
51630     /**
51631      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51632      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51633      * @param {Boolean} preservePanel Overrides the config preservePanel option
51634      * @return {Roo.ContentPanel} The panel that was removed
51635      */
51636     remove : function(panel, preservePanel){
51637         panel = this.getPanel(panel);
51638         if(!panel){
51639             return null;
51640         }
51641         var e = {};
51642         this.fireEvent("beforeremove", this, panel, e);
51643         if(e.cancel === true){
51644             return null;
51645         }
51646         var panelId = panel.getId();
51647         this.panels.removeKey(panelId);
51648         return panel;
51649     },
51650     
51651     /**
51652      * Returns the panel specified or null if it's not in this region.
51653      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51654      * @return {Roo.ContentPanel}
51655      */
51656     getPanel : function(id){
51657         if(typeof id == "object"){ // must be panel obj
51658             return id;
51659         }
51660         return this.panels.get(id);
51661     },
51662     
51663     /**
51664      * Returns this regions position (north/south/east/west/center).
51665      * @return {String} 
51666      */
51667     getPosition: function(){
51668         return this.position;    
51669     }
51670 });/*
51671  * Based on:
51672  * Ext JS Library 1.1.1
51673  * Copyright(c) 2006-2007, Ext JS, LLC.
51674  *
51675  * Originally Released Under LGPL - original licence link has changed is not relivant.
51676  *
51677  * Fork - LGPL
51678  * <script type="text/javascript">
51679  */
51680  
51681 /**
51682  * @class Roo.LayoutRegion
51683  * @extends Roo.BasicLayoutRegion
51684  * This class represents a region in a layout manager.
51685  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51686  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51687  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51688  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51689  * @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})
51690  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51691  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51692  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51693  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51694  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51695  * @cfg {String}    title           The title for the region (overrides panel titles)
51696  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51697  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51698  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51699  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51700  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51701  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51702  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51703  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51704  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51705  * @cfg {Boolean}   showPin         True to show a pin button
51706  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51707  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51708  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51709  * @cfg {Number}    width           For East/West panels
51710  * @cfg {Number}    height          For North/South panels
51711  * @cfg {Boolean}   split           To show the splitter
51712  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51713  */
51714 Roo.LayoutRegion = function(mgr, config, pos){
51715     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51716     var dh = Roo.DomHelper;
51717     /** This region's container element 
51718     * @type Roo.Element */
51719     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51720     /** This region's title element 
51721     * @type Roo.Element */
51722
51723     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51724         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51725         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51726     ]}, true);
51727     this.titleEl.enableDisplayMode();
51728     /** This region's title text element 
51729     * @type HTMLElement */
51730     this.titleTextEl = this.titleEl.dom.firstChild;
51731     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51732     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51733     this.closeBtn.enableDisplayMode();
51734     this.closeBtn.on("click", this.closeClicked, this);
51735     this.closeBtn.hide();
51736
51737     this.createBody(config);
51738     this.visible = true;
51739     this.collapsed = false;
51740
51741     if(config.hideWhenEmpty){
51742         this.hide();
51743         this.on("paneladded", this.validateVisibility, this);
51744         this.on("panelremoved", this.validateVisibility, this);
51745     }
51746     this.applyConfig(config);
51747 };
51748
51749 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51750
51751     createBody : function(){
51752         /** This region's body element 
51753         * @type Roo.Element */
51754         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51755     },
51756
51757     applyConfig : function(c){
51758         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51759             var dh = Roo.DomHelper;
51760             if(c.titlebar !== false){
51761                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51762                 this.collapseBtn.on("click", this.collapse, this);
51763                 this.collapseBtn.enableDisplayMode();
51764
51765                 if(c.showPin === true || this.showPin){
51766                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51767                     this.stickBtn.enableDisplayMode();
51768                     this.stickBtn.on("click", this.expand, this);
51769                     this.stickBtn.hide();
51770                 }
51771             }
51772             /** This region's collapsed element
51773             * @type Roo.Element */
51774             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51775                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51776             ]}, true);
51777             if(c.floatable !== false){
51778                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51779                this.collapsedEl.on("click", this.collapseClick, this);
51780             }
51781
51782             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51783                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51784                    id: "message", unselectable: "on", style:{"float":"left"}});
51785                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51786              }
51787             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51788             this.expandBtn.on("click", this.expand, this);
51789         }
51790         if(this.collapseBtn){
51791             this.collapseBtn.setVisible(c.collapsible == true);
51792         }
51793         this.cmargins = c.cmargins || this.cmargins ||
51794                          (this.position == "west" || this.position == "east" ?
51795                              {top: 0, left: 2, right:2, bottom: 0} :
51796                              {top: 2, left: 0, right:0, bottom: 2});
51797         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51798         this.bottomTabs = c.tabPosition != "top";
51799         this.autoScroll = c.autoScroll || false;
51800         if(this.autoScroll){
51801             this.bodyEl.setStyle("overflow", "auto");
51802         }else{
51803             this.bodyEl.setStyle("overflow", "hidden");
51804         }
51805         //if(c.titlebar !== false){
51806             if((!c.titlebar && !c.title) || c.titlebar === false){
51807                 this.titleEl.hide();
51808             }else{
51809                 this.titleEl.show();
51810                 if(c.title){
51811                     this.titleTextEl.innerHTML = c.title;
51812                 }
51813             }
51814         //}
51815         this.duration = c.duration || .30;
51816         this.slideDuration = c.slideDuration || .45;
51817         this.config = c;
51818         if(c.collapsed){
51819             this.collapse(true);
51820         }
51821         if(c.hidden){
51822             this.hide();
51823         }
51824     },
51825     /**
51826      * Returns true if this region is currently visible.
51827      * @return {Boolean}
51828      */
51829     isVisible : function(){
51830         return this.visible;
51831     },
51832
51833     /**
51834      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51835      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51836      */
51837     setCollapsedTitle : function(title){
51838         title = title || "&#160;";
51839         if(this.collapsedTitleTextEl){
51840             this.collapsedTitleTextEl.innerHTML = title;
51841         }
51842     },
51843
51844     getBox : function(){
51845         var b;
51846         if(!this.collapsed){
51847             b = this.el.getBox(false, true);
51848         }else{
51849             b = this.collapsedEl.getBox(false, true);
51850         }
51851         return b;
51852     },
51853
51854     getMargins : function(){
51855         return this.collapsed ? this.cmargins : this.margins;
51856     },
51857
51858     highlight : function(){
51859         this.el.addClass("x-layout-panel-dragover");
51860     },
51861
51862     unhighlight : function(){
51863         this.el.removeClass("x-layout-panel-dragover");
51864     },
51865
51866     updateBox : function(box){
51867         this.box = box;
51868         if(!this.collapsed){
51869             this.el.dom.style.left = box.x + "px";
51870             this.el.dom.style.top = box.y + "px";
51871             this.updateBody(box.width, box.height);
51872         }else{
51873             this.collapsedEl.dom.style.left = box.x + "px";
51874             this.collapsedEl.dom.style.top = box.y + "px";
51875             this.collapsedEl.setSize(box.width, box.height);
51876         }
51877         if(this.tabs){
51878             this.tabs.autoSizeTabs();
51879         }
51880     },
51881
51882     updateBody : function(w, h){
51883         if(w !== null){
51884             this.el.setWidth(w);
51885             w -= this.el.getBorderWidth("rl");
51886             if(this.config.adjustments){
51887                 w += this.config.adjustments[0];
51888             }
51889         }
51890         if(h !== null){
51891             this.el.setHeight(h);
51892             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
51893             h -= this.el.getBorderWidth("tb");
51894             if(this.config.adjustments){
51895                 h += this.config.adjustments[1];
51896             }
51897             this.bodyEl.setHeight(h);
51898             if(this.tabs){
51899                 h = this.tabs.syncHeight(h);
51900             }
51901         }
51902         if(this.panelSize){
51903             w = w !== null ? w : this.panelSize.width;
51904             h = h !== null ? h : this.panelSize.height;
51905         }
51906         if(this.activePanel){
51907             var el = this.activePanel.getEl();
51908             w = w !== null ? w : el.getWidth();
51909             h = h !== null ? h : el.getHeight();
51910             this.panelSize = {width: w, height: h};
51911             this.activePanel.setSize(w, h);
51912         }
51913         if(Roo.isIE && this.tabs){
51914             this.tabs.el.repaint();
51915         }
51916     },
51917
51918     /**
51919      * Returns the container element for this region.
51920      * @return {Roo.Element}
51921      */
51922     getEl : function(){
51923         return this.el;
51924     },
51925
51926     /**
51927      * Hides this region.
51928      */
51929     hide : function(){
51930         if(!this.collapsed){
51931             this.el.dom.style.left = "-2000px";
51932             this.el.hide();
51933         }else{
51934             this.collapsedEl.dom.style.left = "-2000px";
51935             this.collapsedEl.hide();
51936         }
51937         this.visible = false;
51938         this.fireEvent("visibilitychange", this, false);
51939     },
51940
51941     /**
51942      * Shows this region if it was previously hidden.
51943      */
51944     show : function(){
51945         if(!this.collapsed){
51946             this.el.show();
51947         }else{
51948             this.collapsedEl.show();
51949         }
51950         this.visible = true;
51951         this.fireEvent("visibilitychange", this, true);
51952     },
51953
51954     closeClicked : function(){
51955         if(this.activePanel){
51956             this.remove(this.activePanel);
51957         }
51958     },
51959
51960     collapseClick : function(e){
51961         if(this.isSlid){
51962            e.stopPropagation();
51963            this.slideIn();
51964         }else{
51965            e.stopPropagation();
51966            this.slideOut();
51967         }
51968     },
51969
51970     /**
51971      * Collapses this region.
51972      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
51973      */
51974     collapse : function(skipAnim){
51975         if(this.collapsed) {
51976             return;
51977         }
51978         this.collapsed = true;
51979         if(this.split){
51980             this.split.el.hide();
51981         }
51982         if(this.config.animate && skipAnim !== true){
51983             this.fireEvent("invalidated", this);
51984             this.animateCollapse();
51985         }else{
51986             this.el.setLocation(-20000,-20000);
51987             this.el.hide();
51988             this.collapsedEl.show();
51989             this.fireEvent("collapsed", this);
51990             this.fireEvent("invalidated", this);
51991         }
51992     },
51993
51994     animateCollapse : function(){
51995         // overridden
51996     },
51997
51998     /**
51999      * Expands this region if it was previously collapsed.
52000      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52001      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52002      */
52003     expand : function(e, skipAnim){
52004         if(e) {
52005             e.stopPropagation();
52006         }
52007         if(!this.collapsed || this.el.hasActiveFx()) {
52008             return;
52009         }
52010         if(this.isSlid){
52011             this.afterSlideIn();
52012             skipAnim = true;
52013         }
52014         this.collapsed = false;
52015         if(this.config.animate && skipAnim !== true){
52016             this.animateExpand();
52017         }else{
52018             this.el.show();
52019             if(this.split){
52020                 this.split.el.show();
52021             }
52022             this.collapsedEl.setLocation(-2000,-2000);
52023             this.collapsedEl.hide();
52024             this.fireEvent("invalidated", this);
52025             this.fireEvent("expanded", this);
52026         }
52027     },
52028
52029     animateExpand : function(){
52030         // overridden
52031     },
52032
52033     initTabs : function()
52034     {
52035         this.bodyEl.setStyle("overflow", "hidden");
52036         var ts = new Roo.TabPanel(
52037                 this.bodyEl.dom,
52038                 {
52039                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52040                     disableTooltips: this.config.disableTabTips,
52041                     toolbar : this.config.toolbar
52042                 }
52043         );
52044         if(this.config.hideTabs){
52045             ts.stripWrap.setDisplayed(false);
52046         }
52047         this.tabs = ts;
52048         ts.resizeTabs = this.config.resizeTabs === true;
52049         ts.minTabWidth = this.config.minTabWidth || 40;
52050         ts.maxTabWidth = this.config.maxTabWidth || 250;
52051         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52052         ts.monitorResize = false;
52053         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52054         ts.bodyEl.addClass('x-layout-tabs-body');
52055         this.panels.each(this.initPanelAsTab, this);
52056     },
52057
52058     initPanelAsTab : function(panel){
52059         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52060                     this.config.closeOnTab && panel.isClosable());
52061         if(panel.tabTip !== undefined){
52062             ti.setTooltip(panel.tabTip);
52063         }
52064         ti.on("activate", function(){
52065               this.setActivePanel(panel);
52066         }, this);
52067         if(this.config.closeOnTab){
52068             ti.on("beforeclose", function(t, e){
52069                 e.cancel = true;
52070                 this.remove(panel);
52071             }, this);
52072         }
52073         return ti;
52074     },
52075
52076     updatePanelTitle : function(panel, title){
52077         if(this.activePanel == panel){
52078             this.updateTitle(title);
52079         }
52080         if(this.tabs){
52081             var ti = this.tabs.getTab(panel.getEl().id);
52082             ti.setText(title);
52083             if(panel.tabTip !== undefined){
52084                 ti.setTooltip(panel.tabTip);
52085             }
52086         }
52087     },
52088
52089     updateTitle : function(title){
52090         if(this.titleTextEl && !this.config.title){
52091             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52092         }
52093     },
52094
52095     setActivePanel : function(panel){
52096         panel = this.getPanel(panel);
52097         if(this.activePanel && this.activePanel != panel){
52098             this.activePanel.setActiveState(false);
52099         }
52100         this.activePanel = panel;
52101         panel.setActiveState(true);
52102         if(this.panelSize){
52103             panel.setSize(this.panelSize.width, this.panelSize.height);
52104         }
52105         if(this.closeBtn){
52106             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52107         }
52108         this.updateTitle(panel.getTitle());
52109         if(this.tabs){
52110             this.fireEvent("invalidated", this);
52111         }
52112         this.fireEvent("panelactivated", this, panel);
52113     },
52114
52115     /**
52116      * Shows the specified panel.
52117      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52118      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52119      */
52120     showPanel : function(panel)
52121     {
52122         panel = this.getPanel(panel);
52123         if(panel){
52124             if(this.tabs){
52125                 var tab = this.tabs.getTab(panel.getEl().id);
52126                 if(tab.isHidden()){
52127                     this.tabs.unhideTab(tab.id);
52128                 }
52129                 tab.activate();
52130             }else{
52131                 this.setActivePanel(panel);
52132             }
52133         }
52134         return panel;
52135     },
52136
52137     /**
52138      * Get the active panel for this region.
52139      * @return {Roo.ContentPanel} The active panel or null
52140      */
52141     getActivePanel : function(){
52142         return this.activePanel;
52143     },
52144
52145     validateVisibility : function(){
52146         if(this.panels.getCount() < 1){
52147             this.updateTitle("&#160;");
52148             this.closeBtn.hide();
52149             this.hide();
52150         }else{
52151             if(!this.isVisible()){
52152                 this.show();
52153             }
52154         }
52155     },
52156
52157     /**
52158      * Adds the passed ContentPanel(s) to this region.
52159      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52160      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52161      */
52162     add : function(panel){
52163         if(arguments.length > 1){
52164             for(var i = 0, len = arguments.length; i < len; i++) {
52165                 this.add(arguments[i]);
52166             }
52167             return null;
52168         }
52169         if(this.hasPanel(panel)){
52170             this.showPanel(panel);
52171             return panel;
52172         }
52173         panel.setRegion(this);
52174         this.panels.add(panel);
52175         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52176             this.bodyEl.dom.appendChild(panel.getEl().dom);
52177             if(panel.background !== true){
52178                 this.setActivePanel(panel);
52179             }
52180             this.fireEvent("paneladded", this, panel);
52181             return panel;
52182         }
52183         if(!this.tabs){
52184             this.initTabs();
52185         }else{
52186             this.initPanelAsTab(panel);
52187         }
52188         if(panel.background !== true){
52189             this.tabs.activate(panel.getEl().id);
52190         }
52191         this.fireEvent("paneladded", this, panel);
52192         return panel;
52193     },
52194
52195     /**
52196      * Hides the tab for the specified panel.
52197      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52198      */
52199     hidePanel : function(panel){
52200         if(this.tabs && (panel = this.getPanel(panel))){
52201             this.tabs.hideTab(panel.getEl().id);
52202         }
52203     },
52204
52205     /**
52206      * Unhides the tab for a previously hidden panel.
52207      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52208      */
52209     unhidePanel : function(panel){
52210         if(this.tabs && (panel = this.getPanel(panel))){
52211             this.tabs.unhideTab(panel.getEl().id);
52212         }
52213     },
52214
52215     clearPanels : function(){
52216         while(this.panels.getCount() > 0){
52217              this.remove(this.panels.first());
52218         }
52219     },
52220
52221     /**
52222      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52223      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52224      * @param {Boolean} preservePanel Overrides the config preservePanel option
52225      * @return {Roo.ContentPanel} The panel that was removed
52226      */
52227     remove : function(panel, preservePanel){
52228         panel = this.getPanel(panel);
52229         if(!panel){
52230             return null;
52231         }
52232         var e = {};
52233         this.fireEvent("beforeremove", this, panel, e);
52234         if(e.cancel === true){
52235             return null;
52236         }
52237         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52238         var panelId = panel.getId();
52239         this.panels.removeKey(panelId);
52240         if(preservePanel){
52241             document.body.appendChild(panel.getEl().dom);
52242         }
52243         if(this.tabs){
52244             this.tabs.removeTab(panel.getEl().id);
52245         }else if (!preservePanel){
52246             this.bodyEl.dom.removeChild(panel.getEl().dom);
52247         }
52248         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52249             var p = this.panels.first();
52250             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52251             tempEl.appendChild(p.getEl().dom);
52252             this.bodyEl.update("");
52253             this.bodyEl.dom.appendChild(p.getEl().dom);
52254             tempEl = null;
52255             this.updateTitle(p.getTitle());
52256             this.tabs = null;
52257             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52258             this.setActivePanel(p);
52259         }
52260         panel.setRegion(null);
52261         if(this.activePanel == panel){
52262             this.activePanel = null;
52263         }
52264         if(this.config.autoDestroy !== false && preservePanel !== true){
52265             try{panel.destroy();}catch(e){}
52266         }
52267         this.fireEvent("panelremoved", this, panel);
52268         return panel;
52269     },
52270
52271     /**
52272      * Returns the TabPanel component used by this region
52273      * @return {Roo.TabPanel}
52274      */
52275     getTabs : function(){
52276         return this.tabs;
52277     },
52278
52279     createTool : function(parentEl, className){
52280         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52281             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52282         btn.addClassOnOver("x-layout-tools-button-over");
52283         return btn;
52284     }
52285 });/*
52286  * Based on:
52287  * Ext JS Library 1.1.1
52288  * Copyright(c) 2006-2007, Ext JS, LLC.
52289  *
52290  * Originally Released Under LGPL - original licence link has changed is not relivant.
52291  *
52292  * Fork - LGPL
52293  * <script type="text/javascript">
52294  */
52295  
52296
52297
52298 /**
52299  * @class Roo.SplitLayoutRegion
52300  * @extends Roo.LayoutRegion
52301  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52302  */
52303 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52304     this.cursor = cursor;
52305     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52306 };
52307
52308 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52309     splitTip : "Drag to resize.",
52310     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52311     useSplitTips : false,
52312
52313     applyConfig : function(config){
52314         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52315         if(config.split){
52316             if(!this.split){
52317                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52318                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52319                 /** The SplitBar for this region 
52320                 * @type Roo.SplitBar */
52321                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52322                 this.split.on("moved", this.onSplitMove, this);
52323                 this.split.useShim = config.useShim === true;
52324                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52325                 if(this.useSplitTips){
52326                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52327                 }
52328                 if(config.collapsible){
52329                     this.split.el.on("dblclick", this.collapse,  this);
52330                 }
52331             }
52332             if(typeof config.minSize != "undefined"){
52333                 this.split.minSize = config.minSize;
52334             }
52335             if(typeof config.maxSize != "undefined"){
52336                 this.split.maxSize = config.maxSize;
52337             }
52338             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52339                 this.hideSplitter();
52340             }
52341         }
52342     },
52343
52344     getHMaxSize : function(){
52345          var cmax = this.config.maxSize || 10000;
52346          var center = this.mgr.getRegion("center");
52347          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52348     },
52349
52350     getVMaxSize : function(){
52351          var cmax = this.config.maxSize || 10000;
52352          var center = this.mgr.getRegion("center");
52353          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52354     },
52355
52356     onSplitMove : function(split, newSize){
52357         this.fireEvent("resized", this, newSize);
52358     },
52359     
52360     /** 
52361      * Returns the {@link Roo.SplitBar} for this region.
52362      * @return {Roo.SplitBar}
52363      */
52364     getSplitBar : function(){
52365         return this.split;
52366     },
52367     
52368     hide : function(){
52369         this.hideSplitter();
52370         Roo.SplitLayoutRegion.superclass.hide.call(this);
52371     },
52372
52373     hideSplitter : function(){
52374         if(this.split){
52375             this.split.el.setLocation(-2000,-2000);
52376             this.split.el.hide();
52377         }
52378     },
52379
52380     show : function(){
52381         if(this.split){
52382             this.split.el.show();
52383         }
52384         Roo.SplitLayoutRegion.superclass.show.call(this);
52385     },
52386     
52387     beforeSlide: function(){
52388         if(Roo.isGecko){// firefox overflow auto bug workaround
52389             this.bodyEl.clip();
52390             if(this.tabs) {
52391                 this.tabs.bodyEl.clip();
52392             }
52393             if(this.activePanel){
52394                 this.activePanel.getEl().clip();
52395                 
52396                 if(this.activePanel.beforeSlide){
52397                     this.activePanel.beforeSlide();
52398                 }
52399             }
52400         }
52401     },
52402     
52403     afterSlide : function(){
52404         if(Roo.isGecko){// firefox overflow auto bug workaround
52405             this.bodyEl.unclip();
52406             if(this.tabs) {
52407                 this.tabs.bodyEl.unclip();
52408             }
52409             if(this.activePanel){
52410                 this.activePanel.getEl().unclip();
52411                 if(this.activePanel.afterSlide){
52412                     this.activePanel.afterSlide();
52413                 }
52414             }
52415         }
52416     },
52417
52418     initAutoHide : function(){
52419         if(this.autoHide !== false){
52420             if(!this.autoHideHd){
52421                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52422                 this.autoHideHd = {
52423                     "mouseout": function(e){
52424                         if(!e.within(this.el, true)){
52425                             st.delay(500);
52426                         }
52427                     },
52428                     "mouseover" : function(e){
52429                         st.cancel();
52430                     },
52431                     scope : this
52432                 };
52433             }
52434             this.el.on(this.autoHideHd);
52435         }
52436     },
52437
52438     clearAutoHide : function(){
52439         if(this.autoHide !== false){
52440             this.el.un("mouseout", this.autoHideHd.mouseout);
52441             this.el.un("mouseover", this.autoHideHd.mouseover);
52442         }
52443     },
52444
52445     clearMonitor : function(){
52446         Roo.get(document).un("click", this.slideInIf, this);
52447     },
52448
52449     // these names are backwards but not changed for compat
52450     slideOut : function(){
52451         if(this.isSlid || this.el.hasActiveFx()){
52452             return;
52453         }
52454         this.isSlid = true;
52455         if(this.collapseBtn){
52456             this.collapseBtn.hide();
52457         }
52458         this.closeBtnState = this.closeBtn.getStyle('display');
52459         this.closeBtn.hide();
52460         if(this.stickBtn){
52461             this.stickBtn.show();
52462         }
52463         this.el.show();
52464         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52465         this.beforeSlide();
52466         this.el.setStyle("z-index", 10001);
52467         this.el.slideIn(this.getSlideAnchor(), {
52468             callback: function(){
52469                 this.afterSlide();
52470                 this.initAutoHide();
52471                 Roo.get(document).on("click", this.slideInIf, this);
52472                 this.fireEvent("slideshow", this);
52473             },
52474             scope: this,
52475             block: true
52476         });
52477     },
52478
52479     afterSlideIn : function(){
52480         this.clearAutoHide();
52481         this.isSlid = false;
52482         this.clearMonitor();
52483         this.el.setStyle("z-index", "");
52484         if(this.collapseBtn){
52485             this.collapseBtn.show();
52486         }
52487         this.closeBtn.setStyle('display', this.closeBtnState);
52488         if(this.stickBtn){
52489             this.stickBtn.hide();
52490         }
52491         this.fireEvent("slidehide", this);
52492     },
52493
52494     slideIn : function(cb){
52495         if(!this.isSlid || this.el.hasActiveFx()){
52496             Roo.callback(cb);
52497             return;
52498         }
52499         this.isSlid = false;
52500         this.beforeSlide();
52501         this.el.slideOut(this.getSlideAnchor(), {
52502             callback: function(){
52503                 this.el.setLeftTop(-10000, -10000);
52504                 this.afterSlide();
52505                 this.afterSlideIn();
52506                 Roo.callback(cb);
52507             },
52508             scope: this,
52509             block: true
52510         });
52511     },
52512     
52513     slideInIf : function(e){
52514         if(!e.within(this.el)){
52515             this.slideIn();
52516         }
52517     },
52518
52519     animateCollapse : function(){
52520         this.beforeSlide();
52521         this.el.setStyle("z-index", 20000);
52522         var anchor = this.getSlideAnchor();
52523         this.el.slideOut(anchor, {
52524             callback : function(){
52525                 this.el.setStyle("z-index", "");
52526                 this.collapsedEl.slideIn(anchor, {duration:.3});
52527                 this.afterSlide();
52528                 this.el.setLocation(-10000,-10000);
52529                 this.el.hide();
52530                 this.fireEvent("collapsed", this);
52531             },
52532             scope: this,
52533             block: true
52534         });
52535     },
52536
52537     animateExpand : function(){
52538         this.beforeSlide();
52539         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52540         this.el.setStyle("z-index", 20000);
52541         this.collapsedEl.hide({
52542             duration:.1
52543         });
52544         this.el.slideIn(this.getSlideAnchor(), {
52545             callback : function(){
52546                 this.el.setStyle("z-index", "");
52547                 this.afterSlide();
52548                 if(this.split){
52549                     this.split.el.show();
52550                 }
52551                 this.fireEvent("invalidated", this);
52552                 this.fireEvent("expanded", this);
52553             },
52554             scope: this,
52555             block: true
52556         });
52557     },
52558
52559     anchors : {
52560         "west" : "left",
52561         "east" : "right",
52562         "north" : "top",
52563         "south" : "bottom"
52564     },
52565
52566     sanchors : {
52567         "west" : "l",
52568         "east" : "r",
52569         "north" : "t",
52570         "south" : "b"
52571     },
52572
52573     canchors : {
52574         "west" : "tl-tr",
52575         "east" : "tr-tl",
52576         "north" : "tl-bl",
52577         "south" : "bl-tl"
52578     },
52579
52580     getAnchor : function(){
52581         return this.anchors[this.position];
52582     },
52583
52584     getCollapseAnchor : function(){
52585         return this.canchors[this.position];
52586     },
52587
52588     getSlideAnchor : function(){
52589         return this.sanchors[this.position];
52590     },
52591
52592     getAlignAdj : function(){
52593         var cm = this.cmargins;
52594         switch(this.position){
52595             case "west":
52596                 return [0, 0];
52597             break;
52598             case "east":
52599                 return [0, 0];
52600             break;
52601             case "north":
52602                 return [0, 0];
52603             break;
52604             case "south":
52605                 return [0, 0];
52606             break;
52607         }
52608     },
52609
52610     getExpandAdj : function(){
52611         var c = this.collapsedEl, cm = this.cmargins;
52612         switch(this.position){
52613             case "west":
52614                 return [-(cm.right+c.getWidth()+cm.left), 0];
52615             break;
52616             case "east":
52617                 return [cm.right+c.getWidth()+cm.left, 0];
52618             break;
52619             case "north":
52620                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52621             break;
52622             case "south":
52623                 return [0, cm.top+cm.bottom+c.getHeight()];
52624             break;
52625         }
52626     }
52627 });/*
52628  * Based on:
52629  * Ext JS Library 1.1.1
52630  * Copyright(c) 2006-2007, Ext JS, LLC.
52631  *
52632  * Originally Released Under LGPL - original licence link has changed is not relivant.
52633  *
52634  * Fork - LGPL
52635  * <script type="text/javascript">
52636  */
52637 /*
52638  * These classes are private internal classes
52639  */
52640 Roo.CenterLayoutRegion = function(mgr, config){
52641     Roo.LayoutRegion.call(this, mgr, config, "center");
52642     this.visible = true;
52643     this.minWidth = config.minWidth || 20;
52644     this.minHeight = config.minHeight || 20;
52645 };
52646
52647 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52648     hide : function(){
52649         // center panel can't be hidden
52650     },
52651     
52652     show : function(){
52653         // center panel can't be hidden
52654     },
52655     
52656     getMinWidth: function(){
52657         return this.minWidth;
52658     },
52659     
52660     getMinHeight: function(){
52661         return this.minHeight;
52662     }
52663 });
52664
52665
52666 Roo.NorthLayoutRegion = function(mgr, config){
52667     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52668     if(this.split){
52669         this.split.placement = Roo.SplitBar.TOP;
52670         this.split.orientation = Roo.SplitBar.VERTICAL;
52671         this.split.el.addClass("x-layout-split-v");
52672     }
52673     var size = config.initialSize || config.height;
52674     if(typeof size != "undefined"){
52675         this.el.setHeight(size);
52676     }
52677 };
52678 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52679     orientation: Roo.SplitBar.VERTICAL,
52680     getBox : function(){
52681         if(this.collapsed){
52682             return this.collapsedEl.getBox();
52683         }
52684         var box = this.el.getBox();
52685         if(this.split){
52686             box.height += this.split.el.getHeight();
52687         }
52688         return box;
52689     },
52690     
52691     updateBox : function(box){
52692         if(this.split && !this.collapsed){
52693             box.height -= this.split.el.getHeight();
52694             this.split.el.setLeft(box.x);
52695             this.split.el.setTop(box.y+box.height);
52696             this.split.el.setWidth(box.width);
52697         }
52698         if(this.collapsed){
52699             this.updateBody(box.width, null);
52700         }
52701         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52702     }
52703 });
52704
52705 Roo.SouthLayoutRegion = function(mgr, config){
52706     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52707     if(this.split){
52708         this.split.placement = Roo.SplitBar.BOTTOM;
52709         this.split.orientation = Roo.SplitBar.VERTICAL;
52710         this.split.el.addClass("x-layout-split-v");
52711     }
52712     var size = config.initialSize || config.height;
52713     if(typeof size != "undefined"){
52714         this.el.setHeight(size);
52715     }
52716 };
52717 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52718     orientation: Roo.SplitBar.VERTICAL,
52719     getBox : function(){
52720         if(this.collapsed){
52721             return this.collapsedEl.getBox();
52722         }
52723         var box = this.el.getBox();
52724         if(this.split){
52725             var sh = this.split.el.getHeight();
52726             box.height += sh;
52727             box.y -= sh;
52728         }
52729         return box;
52730     },
52731     
52732     updateBox : function(box){
52733         if(this.split && !this.collapsed){
52734             var sh = this.split.el.getHeight();
52735             box.height -= sh;
52736             box.y += sh;
52737             this.split.el.setLeft(box.x);
52738             this.split.el.setTop(box.y-sh);
52739             this.split.el.setWidth(box.width);
52740         }
52741         if(this.collapsed){
52742             this.updateBody(box.width, null);
52743         }
52744         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52745     }
52746 });
52747
52748 Roo.EastLayoutRegion = function(mgr, config){
52749     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52750     if(this.split){
52751         this.split.placement = Roo.SplitBar.RIGHT;
52752         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52753         this.split.el.addClass("x-layout-split-h");
52754     }
52755     var size = config.initialSize || config.width;
52756     if(typeof size != "undefined"){
52757         this.el.setWidth(size);
52758     }
52759 };
52760 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52761     orientation: Roo.SplitBar.HORIZONTAL,
52762     getBox : function(){
52763         if(this.collapsed){
52764             return this.collapsedEl.getBox();
52765         }
52766         var box = this.el.getBox();
52767         if(this.split){
52768             var sw = this.split.el.getWidth();
52769             box.width += sw;
52770             box.x -= sw;
52771         }
52772         return box;
52773     },
52774
52775     updateBox : function(box){
52776         if(this.split && !this.collapsed){
52777             var sw = this.split.el.getWidth();
52778             box.width -= sw;
52779             this.split.el.setLeft(box.x);
52780             this.split.el.setTop(box.y);
52781             this.split.el.setHeight(box.height);
52782             box.x += sw;
52783         }
52784         if(this.collapsed){
52785             this.updateBody(null, box.height);
52786         }
52787         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52788     }
52789 });
52790
52791 Roo.WestLayoutRegion = function(mgr, config){
52792     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52793     if(this.split){
52794         this.split.placement = Roo.SplitBar.LEFT;
52795         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52796         this.split.el.addClass("x-layout-split-h");
52797     }
52798     var size = config.initialSize || config.width;
52799     if(typeof size != "undefined"){
52800         this.el.setWidth(size);
52801     }
52802 };
52803 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52804     orientation: Roo.SplitBar.HORIZONTAL,
52805     getBox : function(){
52806         if(this.collapsed){
52807             return this.collapsedEl.getBox();
52808         }
52809         var box = this.el.getBox();
52810         if(this.split){
52811             box.width += this.split.el.getWidth();
52812         }
52813         return box;
52814     },
52815     
52816     updateBox : function(box){
52817         if(this.split && !this.collapsed){
52818             var sw = this.split.el.getWidth();
52819             box.width -= sw;
52820             this.split.el.setLeft(box.x+box.width);
52821             this.split.el.setTop(box.y);
52822             this.split.el.setHeight(box.height);
52823         }
52824         if(this.collapsed){
52825             this.updateBody(null, box.height);
52826         }
52827         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52828     }
52829 });
52830 /*
52831  * Based on:
52832  * Ext JS Library 1.1.1
52833  * Copyright(c) 2006-2007, Ext JS, LLC.
52834  *
52835  * Originally Released Under LGPL - original licence link has changed is not relivant.
52836  *
52837  * Fork - LGPL
52838  * <script type="text/javascript">
52839  */
52840  
52841  
52842 /*
52843  * Private internal class for reading and applying state
52844  */
52845 Roo.LayoutStateManager = function(layout){
52846      // default empty state
52847      this.state = {
52848         north: {},
52849         south: {},
52850         east: {},
52851         west: {}       
52852     };
52853 };
52854
52855 Roo.LayoutStateManager.prototype = {
52856     init : function(layout, provider){
52857         this.provider = provider;
52858         var state = provider.get(layout.id+"-layout-state");
52859         if(state){
52860             var wasUpdating = layout.isUpdating();
52861             if(!wasUpdating){
52862                 layout.beginUpdate();
52863             }
52864             for(var key in state){
52865                 if(typeof state[key] != "function"){
52866                     var rstate = state[key];
52867                     var r = layout.getRegion(key);
52868                     if(r && rstate){
52869                         if(rstate.size){
52870                             r.resizeTo(rstate.size);
52871                         }
52872                         if(rstate.collapsed == true){
52873                             r.collapse(true);
52874                         }else{
52875                             r.expand(null, true);
52876                         }
52877                     }
52878                 }
52879             }
52880             if(!wasUpdating){
52881                 layout.endUpdate();
52882             }
52883             this.state = state; 
52884         }
52885         this.layout = layout;
52886         layout.on("regionresized", this.onRegionResized, this);
52887         layout.on("regioncollapsed", this.onRegionCollapsed, this);
52888         layout.on("regionexpanded", this.onRegionExpanded, this);
52889     },
52890     
52891     storeState : function(){
52892         this.provider.set(this.layout.id+"-layout-state", this.state);
52893     },
52894     
52895     onRegionResized : function(region, newSize){
52896         this.state[region.getPosition()].size = newSize;
52897         this.storeState();
52898     },
52899     
52900     onRegionCollapsed : function(region){
52901         this.state[region.getPosition()].collapsed = true;
52902         this.storeState();
52903     },
52904     
52905     onRegionExpanded : function(region){
52906         this.state[region.getPosition()].collapsed = false;
52907         this.storeState();
52908     }
52909 };/*
52910  * Based on:
52911  * Ext JS Library 1.1.1
52912  * Copyright(c) 2006-2007, Ext JS, LLC.
52913  *
52914  * Originally Released Under LGPL - original licence link has changed is not relivant.
52915  *
52916  * Fork - LGPL
52917  * <script type="text/javascript">
52918  */
52919 /**
52920  * @class Roo.ContentPanel
52921  * @extends Roo.util.Observable
52922  * A basic ContentPanel element.
52923  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
52924  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
52925  * @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
52926  * @cfg {Boolean}   closable      True if the panel can be closed/removed
52927  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
52928  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
52929  * @cfg {Toolbar}   toolbar       A toolbar for this panel
52930  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
52931  * @cfg {String} title          The title for this panel
52932  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
52933  * @cfg {String} url            Calls {@link #setUrl} with this value
52934  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
52935  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
52936  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
52937  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
52938
52939  * @constructor
52940  * Create a new ContentPanel.
52941  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
52942  * @param {String/Object} config A string to set only the title or a config object
52943  * @param {String} content (optional) Set the HTML content for this panel
52944  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
52945  */
52946 Roo.ContentPanel = function(el, config, content){
52947     
52948      
52949     /*
52950     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
52951         config = el;
52952         el = Roo.id();
52953     }
52954     if (config && config.parentLayout) { 
52955         el = config.parentLayout.el.createChild(); 
52956     }
52957     */
52958     if(el.autoCreate){ // xtype is available if this is called from factory
52959         config = el;
52960         el = Roo.id();
52961     }
52962     this.el = Roo.get(el);
52963     if(!this.el && config && config.autoCreate){
52964         if(typeof config.autoCreate == "object"){
52965             if(!config.autoCreate.id){
52966                 config.autoCreate.id = config.id||el;
52967             }
52968             this.el = Roo.DomHelper.append(document.body,
52969                         config.autoCreate, true);
52970         }else{
52971             this.el = Roo.DomHelper.append(document.body,
52972                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
52973         }
52974     }
52975     this.closable = false;
52976     this.loaded = false;
52977     this.active = false;
52978     if(typeof config == "string"){
52979         this.title = config;
52980     }else{
52981         Roo.apply(this, config);
52982     }
52983     
52984     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
52985         this.wrapEl = this.el.wrap();
52986         this.toolbar.container = this.el.insertSibling(false, 'before');
52987         this.toolbar = new Roo.Toolbar(this.toolbar);
52988     }
52989     
52990     // xtype created footer. - not sure if will work as we normally have to render first..
52991     if (this.footer && !this.footer.el && this.footer.xtype) {
52992         if (!this.wrapEl) {
52993             this.wrapEl = this.el.wrap();
52994         }
52995     
52996         this.footer.container = this.wrapEl.createChild();
52997          
52998         this.footer = Roo.factory(this.footer, Roo);
52999         
53000     }
53001     
53002     if(this.resizeEl){
53003         this.resizeEl = Roo.get(this.resizeEl, true);
53004     }else{
53005         this.resizeEl = this.el;
53006     }
53007     // handle view.xtype
53008     
53009  
53010     
53011     
53012     this.addEvents({
53013         /**
53014          * @event activate
53015          * Fires when this panel is activated. 
53016          * @param {Roo.ContentPanel} this
53017          */
53018         "activate" : true,
53019         /**
53020          * @event deactivate
53021          * Fires when this panel is activated. 
53022          * @param {Roo.ContentPanel} this
53023          */
53024         "deactivate" : true,
53025
53026         /**
53027          * @event resize
53028          * Fires when this panel is resized if fitToFrame is true.
53029          * @param {Roo.ContentPanel} this
53030          * @param {Number} width The width after any component adjustments
53031          * @param {Number} height The height after any component adjustments
53032          */
53033         "resize" : true,
53034         
53035          /**
53036          * @event render
53037          * Fires when this tab is created
53038          * @param {Roo.ContentPanel} this
53039          */
53040         "render" : true
53041         
53042         
53043         
53044     });
53045     
53046
53047     
53048     
53049     if(this.autoScroll){
53050         this.resizeEl.setStyle("overflow", "auto");
53051     } else {
53052         // fix randome scrolling
53053         this.el.on('scroll', function() {
53054             Roo.log('fix random scolling');
53055             this.scrollTo('top',0); 
53056         });
53057     }
53058     content = content || this.content;
53059     if(content){
53060         this.setContent(content);
53061     }
53062     if(config && config.url){
53063         this.setUrl(this.url, this.params, this.loadOnce);
53064     }
53065     
53066     
53067     
53068     Roo.ContentPanel.superclass.constructor.call(this);
53069     
53070     if (this.view && typeof(this.view.xtype) != 'undefined') {
53071         this.view.el = this.el.appendChild(document.createElement("div"));
53072         this.view = Roo.factory(this.view); 
53073         this.view.render  &&  this.view.render(false, '');  
53074     }
53075     
53076     
53077     this.fireEvent('render', this);
53078 };
53079
53080 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53081     tabTip:'',
53082     setRegion : function(region){
53083         this.region = region;
53084         if(region){
53085            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53086         }else{
53087            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53088         } 
53089     },
53090     
53091     /**
53092      * Returns the toolbar for this Panel if one was configured. 
53093      * @return {Roo.Toolbar} 
53094      */
53095     getToolbar : function(){
53096         return this.toolbar;
53097     },
53098     
53099     setActiveState : function(active){
53100         this.active = active;
53101         if(!active){
53102             this.fireEvent("deactivate", this);
53103         }else{
53104             this.fireEvent("activate", this);
53105         }
53106     },
53107     /**
53108      * Updates this panel's element
53109      * @param {String} content The new content
53110      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53111     */
53112     setContent : function(content, loadScripts){
53113         this.el.update(content, loadScripts);
53114     },
53115
53116     ignoreResize : function(w, h){
53117         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53118             return true;
53119         }else{
53120             this.lastSize = {width: w, height: h};
53121             return false;
53122         }
53123     },
53124     /**
53125      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53126      * @return {Roo.UpdateManager} The UpdateManager
53127      */
53128     getUpdateManager : function(){
53129         return this.el.getUpdateManager();
53130     },
53131      /**
53132      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53133      * @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:
53134 <pre><code>
53135 panel.load({
53136     url: "your-url.php",
53137     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53138     callback: yourFunction,
53139     scope: yourObject, //(optional scope)
53140     discardUrl: false,
53141     nocache: false,
53142     text: "Loading...",
53143     timeout: 30,
53144     scripts: false
53145 });
53146 </code></pre>
53147      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53148      * 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.
53149      * @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}
53150      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53151      * @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.
53152      * @return {Roo.ContentPanel} this
53153      */
53154     load : function(){
53155         var um = this.el.getUpdateManager();
53156         um.update.apply(um, arguments);
53157         return this;
53158     },
53159
53160
53161     /**
53162      * 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.
53163      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53164      * @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)
53165      * @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)
53166      * @return {Roo.UpdateManager} The UpdateManager
53167      */
53168     setUrl : function(url, params, loadOnce){
53169         if(this.refreshDelegate){
53170             this.removeListener("activate", this.refreshDelegate);
53171         }
53172         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53173         this.on("activate", this.refreshDelegate);
53174         return this.el.getUpdateManager();
53175     },
53176     
53177     _handleRefresh : function(url, params, loadOnce){
53178         if(!loadOnce || !this.loaded){
53179             var updater = this.el.getUpdateManager();
53180             updater.update(url, params, this._setLoaded.createDelegate(this));
53181         }
53182     },
53183     
53184     _setLoaded : function(){
53185         this.loaded = true;
53186     }, 
53187     
53188     /**
53189      * Returns this panel's id
53190      * @return {String} 
53191      */
53192     getId : function(){
53193         return this.el.id;
53194     },
53195     
53196     /** 
53197      * Returns this panel's element - used by regiosn to add.
53198      * @return {Roo.Element} 
53199      */
53200     getEl : function(){
53201         return this.wrapEl || this.el;
53202     },
53203     
53204     adjustForComponents : function(width, height)
53205     {
53206         //Roo.log('adjustForComponents ');
53207         if(this.resizeEl != this.el){
53208             width -= this.el.getFrameWidth('lr');
53209             height -= this.el.getFrameWidth('tb');
53210         }
53211         if(this.toolbar){
53212             var te = this.toolbar.getEl();
53213             height -= te.getHeight();
53214             te.setWidth(width);
53215         }
53216         if(this.footer){
53217             var te = this.footer.getEl();
53218             Roo.log("footer:" + te.getHeight());
53219             
53220             height -= te.getHeight();
53221             te.setWidth(width);
53222         }
53223         
53224         
53225         if(this.adjustments){
53226             width += this.adjustments[0];
53227             height += this.adjustments[1];
53228         }
53229         return {"width": width, "height": height};
53230     },
53231     
53232     setSize : function(width, height){
53233         if(this.fitToFrame && !this.ignoreResize(width, height)){
53234             if(this.fitContainer && this.resizeEl != this.el){
53235                 this.el.setSize(width, height);
53236             }
53237             var size = this.adjustForComponents(width, height);
53238             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53239             this.fireEvent('resize', this, size.width, size.height);
53240         }
53241     },
53242     
53243     /**
53244      * Returns this panel's title
53245      * @return {String} 
53246      */
53247     getTitle : function(){
53248         return this.title;
53249     },
53250     
53251     /**
53252      * Set this panel's title
53253      * @param {String} title
53254      */
53255     setTitle : function(title){
53256         this.title = title;
53257         if(this.region){
53258             this.region.updatePanelTitle(this, title);
53259         }
53260     },
53261     
53262     /**
53263      * Returns true is this panel was configured to be closable
53264      * @return {Boolean} 
53265      */
53266     isClosable : function(){
53267         return this.closable;
53268     },
53269     
53270     beforeSlide : function(){
53271         this.el.clip();
53272         this.resizeEl.clip();
53273     },
53274     
53275     afterSlide : function(){
53276         this.el.unclip();
53277         this.resizeEl.unclip();
53278     },
53279     
53280     /**
53281      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53282      *   Will fail silently if the {@link #setUrl} method has not been called.
53283      *   This does not activate the panel, just updates its content.
53284      */
53285     refresh : function(){
53286         if(this.refreshDelegate){
53287            this.loaded = false;
53288            this.refreshDelegate();
53289         }
53290     },
53291     
53292     /**
53293      * Destroys this panel
53294      */
53295     destroy : function(){
53296         this.el.removeAllListeners();
53297         var tempEl = document.createElement("span");
53298         tempEl.appendChild(this.el.dom);
53299         tempEl.innerHTML = "";
53300         this.el.remove();
53301         this.el = null;
53302     },
53303     
53304     /**
53305      * form - if the content panel contains a form - this is a reference to it.
53306      * @type {Roo.form.Form}
53307      */
53308     form : false,
53309     /**
53310      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53311      *    This contains a reference to it.
53312      * @type {Roo.View}
53313      */
53314     view : false,
53315     
53316       /**
53317      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53318      * <pre><code>
53319
53320 layout.addxtype({
53321        xtype : 'Form',
53322        items: [ .... ]
53323    }
53324 );
53325
53326 </code></pre>
53327      * @param {Object} cfg Xtype definition of item to add.
53328      */
53329     
53330     addxtype : function(cfg) {
53331         // add form..
53332         if (cfg.xtype.match(/^Form$/)) {
53333             
53334             var el;
53335             //if (this.footer) {
53336             //    el = this.footer.container.insertSibling(false, 'before');
53337             //} else {
53338                 el = this.el.createChild();
53339             //}
53340
53341             this.form = new  Roo.form.Form(cfg);
53342             
53343             
53344             if ( this.form.allItems.length) {
53345                 this.form.render(el.dom);
53346             }
53347             return this.form;
53348         }
53349         // should only have one of theses..
53350         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53351             // views.. should not be just added - used named prop 'view''
53352             
53353             cfg.el = this.el.appendChild(document.createElement("div"));
53354             // factory?
53355             
53356             var ret = new Roo.factory(cfg);
53357              
53358              ret.render && ret.render(false, ''); // render blank..
53359             this.view = ret;
53360             return ret;
53361         }
53362         return false;
53363     }
53364 });
53365
53366 /**
53367  * @class Roo.GridPanel
53368  * @extends Roo.ContentPanel
53369  * @constructor
53370  * Create a new GridPanel.
53371  * @param {Roo.grid.Grid} grid The grid for this panel
53372  * @param {String/Object} config A string to set only the panel's title, or a config object
53373  */
53374 Roo.GridPanel = function(grid, config){
53375     
53376   
53377     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53378         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53379         
53380     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53381     
53382     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53383     
53384     if(this.toolbar){
53385         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53386     }
53387     // xtype created footer. - not sure if will work as we normally have to render first..
53388     if (this.footer && !this.footer.el && this.footer.xtype) {
53389         
53390         this.footer.container = this.grid.getView().getFooterPanel(true);
53391         this.footer.dataSource = this.grid.dataSource;
53392         this.footer = Roo.factory(this.footer, Roo);
53393         
53394     }
53395     
53396     grid.monitorWindowResize = false; // turn off autosizing
53397     grid.autoHeight = false;
53398     grid.autoWidth = false;
53399     this.grid = grid;
53400     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53401 };
53402
53403 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53404     getId : function(){
53405         return this.grid.id;
53406     },
53407     
53408     /**
53409      * Returns the grid for this panel
53410      * @return {Roo.grid.Grid} 
53411      */
53412     getGrid : function(){
53413         return this.grid;    
53414     },
53415     
53416     setSize : function(width, height){
53417         if(!this.ignoreResize(width, height)){
53418             var grid = this.grid;
53419             var size = this.adjustForComponents(width, height);
53420             grid.getGridEl().setSize(size.width, size.height);
53421             grid.autoSize();
53422         }
53423     },
53424     
53425     beforeSlide : function(){
53426         this.grid.getView().scroller.clip();
53427     },
53428     
53429     afterSlide : function(){
53430         this.grid.getView().scroller.unclip();
53431     },
53432     
53433     destroy : function(){
53434         this.grid.destroy();
53435         delete this.grid;
53436         Roo.GridPanel.superclass.destroy.call(this); 
53437     }
53438 });
53439
53440
53441 /**
53442  * @class Roo.NestedLayoutPanel
53443  * @extends Roo.ContentPanel
53444  * @constructor
53445  * Create a new NestedLayoutPanel.
53446  * 
53447  * 
53448  * @param {Roo.BorderLayout} layout The layout for this panel
53449  * @param {String/Object} config A string to set only the title or a config object
53450  */
53451 Roo.NestedLayoutPanel = function(layout, config)
53452 {
53453     // construct with only one argument..
53454     /* FIXME - implement nicer consturctors
53455     if (layout.layout) {
53456         config = layout;
53457         layout = config.layout;
53458         delete config.layout;
53459     }
53460     if (layout.xtype && !layout.getEl) {
53461         // then layout needs constructing..
53462         layout = Roo.factory(layout, Roo);
53463     }
53464     */
53465     
53466     
53467     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53468     
53469     layout.monitorWindowResize = false; // turn off autosizing
53470     this.layout = layout;
53471     this.layout.getEl().addClass("x-layout-nested-layout");
53472     
53473     
53474     
53475     
53476 };
53477
53478 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53479
53480     setSize : function(width, height){
53481         if(!this.ignoreResize(width, height)){
53482             var size = this.adjustForComponents(width, height);
53483             var el = this.layout.getEl();
53484             el.setSize(size.width, size.height);
53485             var touch = el.dom.offsetWidth;
53486             this.layout.layout();
53487             // ie requires a double layout on the first pass
53488             if(Roo.isIE && !this.initialized){
53489                 this.initialized = true;
53490                 this.layout.layout();
53491             }
53492         }
53493     },
53494     
53495     // activate all subpanels if not currently active..
53496     
53497     setActiveState : function(active){
53498         this.active = active;
53499         if(!active){
53500             this.fireEvent("deactivate", this);
53501             return;
53502         }
53503         
53504         this.fireEvent("activate", this);
53505         // not sure if this should happen before or after..
53506         if (!this.layout) {
53507             return; // should not happen..
53508         }
53509         var reg = false;
53510         for (var r in this.layout.regions) {
53511             reg = this.layout.getRegion(r);
53512             if (reg.getActivePanel()) {
53513                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53514                 reg.setActivePanel(reg.getActivePanel());
53515                 continue;
53516             }
53517             if (!reg.panels.length) {
53518                 continue;
53519             }
53520             reg.showPanel(reg.getPanel(0));
53521         }
53522         
53523         
53524         
53525         
53526     },
53527     
53528     /**
53529      * Returns the nested BorderLayout for this panel
53530      * @return {Roo.BorderLayout} 
53531      */
53532     getLayout : function(){
53533         return this.layout;
53534     },
53535     
53536      /**
53537      * Adds a xtype elements to the layout of the nested panel
53538      * <pre><code>
53539
53540 panel.addxtype({
53541        xtype : 'ContentPanel',
53542        region: 'west',
53543        items: [ .... ]
53544    }
53545 );
53546
53547 panel.addxtype({
53548         xtype : 'NestedLayoutPanel',
53549         region: 'west',
53550         layout: {
53551            center: { },
53552            west: { }   
53553         },
53554         items : [ ... list of content panels or nested layout panels.. ]
53555    }
53556 );
53557 </code></pre>
53558      * @param {Object} cfg Xtype definition of item to add.
53559      */
53560     addxtype : function(cfg) {
53561         return this.layout.addxtype(cfg);
53562     
53563     }
53564 });
53565
53566 Roo.ScrollPanel = function(el, config, content){
53567     config = config || {};
53568     config.fitToFrame = true;
53569     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53570     
53571     this.el.dom.style.overflow = "hidden";
53572     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53573     this.el.removeClass("x-layout-inactive-content");
53574     this.el.on("mousewheel", this.onWheel, this);
53575
53576     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53577     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53578     up.unselectable(); down.unselectable();
53579     up.on("click", this.scrollUp, this);
53580     down.on("click", this.scrollDown, this);
53581     up.addClassOnOver("x-scroller-btn-over");
53582     down.addClassOnOver("x-scroller-btn-over");
53583     up.addClassOnClick("x-scroller-btn-click");
53584     down.addClassOnClick("x-scroller-btn-click");
53585     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53586
53587     this.resizeEl = this.el;
53588     this.el = wrap; this.up = up; this.down = down;
53589 };
53590
53591 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53592     increment : 100,
53593     wheelIncrement : 5,
53594     scrollUp : function(){
53595         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53596     },
53597
53598     scrollDown : function(){
53599         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53600     },
53601
53602     afterScroll : function(){
53603         var el = this.resizeEl;
53604         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53605         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53606         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53607     },
53608
53609     setSize : function(){
53610         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53611         this.afterScroll();
53612     },
53613
53614     onWheel : function(e){
53615         var d = e.getWheelDelta();
53616         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53617         this.afterScroll();
53618         e.stopEvent();
53619     },
53620
53621     setContent : function(content, loadScripts){
53622         this.resizeEl.update(content, loadScripts);
53623     }
53624
53625 });
53626
53627
53628
53629
53630
53631
53632
53633
53634
53635 /**
53636  * @class Roo.TreePanel
53637  * @extends Roo.ContentPanel
53638  * @constructor
53639  * Create a new TreePanel. - defaults to fit/scoll contents.
53640  * @param {String/Object} config A string to set only the panel's title, or a config object
53641  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53642  */
53643 Roo.TreePanel = function(config){
53644     var el = config.el;
53645     var tree = config.tree;
53646     delete config.tree; 
53647     delete config.el; // hopefull!
53648     
53649     // wrapper for IE7 strict & safari scroll issue
53650     
53651     var treeEl = el.createChild();
53652     config.resizeEl = treeEl;
53653     
53654     
53655     
53656     Roo.TreePanel.superclass.constructor.call(this, el, config);
53657  
53658  
53659     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53660     //console.log(tree);
53661     this.on('activate', function()
53662     {
53663         if (this.tree.rendered) {
53664             return;
53665         }
53666         //console.log('render tree');
53667         this.tree.render();
53668     });
53669     // this should not be needed.. - it's actually the 'el' that resizes?
53670     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53671     
53672     //this.on('resize',  function (cp, w, h) {
53673     //        this.tree.innerCt.setWidth(w);
53674     //        this.tree.innerCt.setHeight(h);
53675     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53676     //});
53677
53678         
53679     
53680 };
53681
53682 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53683     fitToFrame : true,
53684     autoScroll : true
53685 });
53686
53687
53688
53689
53690
53691
53692
53693
53694
53695
53696
53697 /*
53698  * Based on:
53699  * Ext JS Library 1.1.1
53700  * Copyright(c) 2006-2007, Ext JS, LLC.
53701  *
53702  * Originally Released Under LGPL - original licence link has changed is not relivant.
53703  *
53704  * Fork - LGPL
53705  * <script type="text/javascript">
53706  */
53707  
53708
53709 /**
53710  * @class Roo.ReaderLayout
53711  * @extends Roo.BorderLayout
53712  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53713  * center region containing two nested regions (a top one for a list view and one for item preview below),
53714  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53715  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53716  * expedites the setup of the overall layout and regions for this common application style.
53717  * Example:
53718  <pre><code>
53719 var reader = new Roo.ReaderLayout();
53720 var CP = Roo.ContentPanel;  // shortcut for adding
53721
53722 reader.beginUpdate();
53723 reader.add("north", new CP("north", "North"));
53724 reader.add("west", new CP("west", {title: "West"}));
53725 reader.add("east", new CP("east", {title: "East"}));
53726
53727 reader.regions.listView.add(new CP("listView", "List"));
53728 reader.regions.preview.add(new CP("preview", "Preview"));
53729 reader.endUpdate();
53730 </code></pre>
53731 * @constructor
53732 * Create a new ReaderLayout
53733 * @param {Object} config Configuration options
53734 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53735 * document.body if omitted)
53736 */
53737 Roo.ReaderLayout = function(config, renderTo){
53738     var c = config || {size:{}};
53739     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53740         north: c.north !== false ? Roo.apply({
53741             split:false,
53742             initialSize: 32,
53743             titlebar: false
53744         }, c.north) : false,
53745         west: c.west !== false ? Roo.apply({
53746             split:true,
53747             initialSize: 200,
53748             minSize: 175,
53749             maxSize: 400,
53750             titlebar: true,
53751             collapsible: true,
53752             animate: true,
53753             margins:{left:5,right:0,bottom:5,top:5},
53754             cmargins:{left:5,right:5,bottom:5,top:5}
53755         }, c.west) : false,
53756         east: c.east !== false ? Roo.apply({
53757             split:true,
53758             initialSize: 200,
53759             minSize: 175,
53760             maxSize: 400,
53761             titlebar: true,
53762             collapsible: true,
53763             animate: true,
53764             margins:{left:0,right:5,bottom:5,top:5},
53765             cmargins:{left:5,right:5,bottom:5,top:5}
53766         }, c.east) : false,
53767         center: Roo.apply({
53768             tabPosition: 'top',
53769             autoScroll:false,
53770             closeOnTab: true,
53771             titlebar:false,
53772             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53773         }, c.center)
53774     });
53775
53776     this.el.addClass('x-reader');
53777
53778     this.beginUpdate();
53779
53780     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53781         south: c.preview !== false ? Roo.apply({
53782             split:true,
53783             initialSize: 200,
53784             minSize: 100,
53785             autoScroll:true,
53786             collapsible:true,
53787             titlebar: true,
53788             cmargins:{top:5,left:0, right:0, bottom:0}
53789         }, c.preview) : false,
53790         center: Roo.apply({
53791             autoScroll:false,
53792             titlebar:false,
53793             minHeight:200
53794         }, c.listView)
53795     });
53796     this.add('center', new Roo.NestedLayoutPanel(inner,
53797             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53798
53799     this.endUpdate();
53800
53801     this.regions.preview = inner.getRegion('south');
53802     this.regions.listView = inner.getRegion('center');
53803 };
53804
53805 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53806  * Based on:
53807  * Ext JS Library 1.1.1
53808  * Copyright(c) 2006-2007, Ext JS, LLC.
53809  *
53810  * Originally Released Under LGPL - original licence link has changed is not relivant.
53811  *
53812  * Fork - LGPL
53813  * <script type="text/javascript">
53814  */
53815  
53816 /**
53817  * @class Roo.grid.Grid
53818  * @extends Roo.util.Observable
53819  * This class represents the primary interface of a component based grid control.
53820  * <br><br>Usage:<pre><code>
53821  var grid = new Roo.grid.Grid("my-container-id", {
53822      ds: myDataStore,
53823      cm: myColModel,
53824      selModel: mySelectionModel,
53825      autoSizeColumns: true,
53826      monitorWindowResize: false,
53827      trackMouseOver: true
53828  });
53829  // set any options
53830  grid.render();
53831  * </code></pre>
53832  * <b>Common Problems:</b><br/>
53833  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53834  * element will correct this<br/>
53835  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53836  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53837  * are unpredictable.<br/>
53838  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53839  * grid to calculate dimensions/offsets.<br/>
53840   * @constructor
53841  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53842  * The container MUST have some type of size defined for the grid to fill. The container will be
53843  * automatically set to position relative if it isn't already.
53844  * @param {Object} config A config object that sets properties on this grid.
53845  */
53846 Roo.grid.Grid = function(container, config){
53847         // initialize the container
53848         this.container = Roo.get(container);
53849         this.container.update("");
53850         this.container.setStyle("overflow", "hidden");
53851     this.container.addClass('x-grid-container');
53852
53853     this.id = this.container.id;
53854
53855     Roo.apply(this, config);
53856     // check and correct shorthanded configs
53857     if(this.ds){
53858         this.dataSource = this.ds;
53859         delete this.ds;
53860     }
53861     if(this.cm){
53862         this.colModel = this.cm;
53863         delete this.cm;
53864     }
53865     if(this.sm){
53866         this.selModel = this.sm;
53867         delete this.sm;
53868     }
53869
53870     if (this.selModel) {
53871         this.selModel = Roo.factory(this.selModel, Roo.grid);
53872         this.sm = this.selModel;
53873         this.sm.xmodule = this.xmodule || false;
53874     }
53875     if (typeof(this.colModel.config) == 'undefined') {
53876         this.colModel = new Roo.grid.ColumnModel(this.colModel);
53877         this.cm = this.colModel;
53878         this.cm.xmodule = this.xmodule || false;
53879     }
53880     if (this.dataSource) {
53881         this.dataSource= Roo.factory(this.dataSource, Roo.data);
53882         this.ds = this.dataSource;
53883         this.ds.xmodule = this.xmodule || false;
53884          
53885     }
53886     
53887     
53888     
53889     if(this.width){
53890         this.container.setWidth(this.width);
53891     }
53892
53893     if(this.height){
53894         this.container.setHeight(this.height);
53895     }
53896     /** @private */
53897         this.addEvents({
53898         // raw events
53899         /**
53900          * @event click
53901          * The raw click event for the entire grid.
53902          * @param {Roo.EventObject} e
53903          */
53904         "click" : true,
53905         /**
53906          * @event dblclick
53907          * The raw dblclick event for the entire grid.
53908          * @param {Roo.EventObject} e
53909          */
53910         "dblclick" : true,
53911         /**
53912          * @event contextmenu
53913          * The raw contextmenu event for the entire grid.
53914          * @param {Roo.EventObject} e
53915          */
53916         "contextmenu" : true,
53917         /**
53918          * @event mousedown
53919          * The raw mousedown event for the entire grid.
53920          * @param {Roo.EventObject} e
53921          */
53922         "mousedown" : true,
53923         /**
53924          * @event mouseup
53925          * The raw mouseup event for the entire grid.
53926          * @param {Roo.EventObject} e
53927          */
53928         "mouseup" : true,
53929         /**
53930          * @event mouseover
53931          * The raw mouseover event for the entire grid.
53932          * @param {Roo.EventObject} e
53933          */
53934         "mouseover" : true,
53935         /**
53936          * @event mouseout
53937          * The raw mouseout event for the entire grid.
53938          * @param {Roo.EventObject} e
53939          */
53940         "mouseout" : true,
53941         /**
53942          * @event keypress
53943          * The raw keypress event for the entire grid.
53944          * @param {Roo.EventObject} e
53945          */
53946         "keypress" : true,
53947         /**
53948          * @event keydown
53949          * The raw keydown event for the entire grid.
53950          * @param {Roo.EventObject} e
53951          */
53952         "keydown" : true,
53953
53954         // custom events
53955
53956         /**
53957          * @event cellclick
53958          * Fires when a cell is clicked
53959          * @param {Grid} this
53960          * @param {Number} rowIndex
53961          * @param {Number} columnIndex
53962          * @param {Roo.EventObject} e
53963          */
53964         "cellclick" : true,
53965         /**
53966          * @event celldblclick
53967          * Fires when a cell is double clicked
53968          * @param {Grid} this
53969          * @param {Number} rowIndex
53970          * @param {Number} columnIndex
53971          * @param {Roo.EventObject} e
53972          */
53973         "celldblclick" : true,
53974         /**
53975          * @event rowclick
53976          * Fires when a row is clicked
53977          * @param {Grid} this
53978          * @param {Number} rowIndex
53979          * @param {Roo.EventObject} e
53980          */
53981         "rowclick" : true,
53982         /**
53983          * @event rowdblclick
53984          * Fires when a row is double clicked
53985          * @param {Grid} this
53986          * @param {Number} rowIndex
53987          * @param {Roo.EventObject} e
53988          */
53989         "rowdblclick" : true,
53990         /**
53991          * @event headerclick
53992          * Fires when a header is clicked
53993          * @param {Grid} this
53994          * @param {Number} columnIndex
53995          * @param {Roo.EventObject} e
53996          */
53997         "headerclick" : true,
53998         /**
53999          * @event headerdblclick
54000          * Fires when a header cell is double clicked
54001          * @param {Grid} this
54002          * @param {Number} columnIndex
54003          * @param {Roo.EventObject} e
54004          */
54005         "headerdblclick" : true,
54006         /**
54007          * @event rowcontextmenu
54008          * Fires when a row is right clicked
54009          * @param {Grid} this
54010          * @param {Number} rowIndex
54011          * @param {Roo.EventObject} e
54012          */
54013         "rowcontextmenu" : true,
54014         /**
54015          * @event cellcontextmenu
54016          * Fires when a cell is right clicked
54017          * @param {Grid} this
54018          * @param {Number} rowIndex
54019          * @param {Number} cellIndex
54020          * @param {Roo.EventObject} e
54021          */
54022          "cellcontextmenu" : true,
54023         /**
54024          * @event headercontextmenu
54025          * Fires when a header is right clicked
54026          * @param {Grid} this
54027          * @param {Number} columnIndex
54028          * @param {Roo.EventObject} e
54029          */
54030         "headercontextmenu" : true,
54031         /**
54032          * @event bodyscroll
54033          * Fires when the body element is scrolled
54034          * @param {Number} scrollLeft
54035          * @param {Number} scrollTop
54036          */
54037         "bodyscroll" : true,
54038         /**
54039          * @event columnresize
54040          * Fires when the user resizes a column
54041          * @param {Number} columnIndex
54042          * @param {Number} newSize
54043          */
54044         "columnresize" : true,
54045         /**
54046          * @event columnmove
54047          * Fires when the user moves a column
54048          * @param {Number} oldIndex
54049          * @param {Number} newIndex
54050          */
54051         "columnmove" : true,
54052         /**
54053          * @event startdrag
54054          * Fires when row(s) start being dragged
54055          * @param {Grid} this
54056          * @param {Roo.GridDD} dd The drag drop object
54057          * @param {event} e The raw browser event
54058          */
54059         "startdrag" : true,
54060         /**
54061          * @event enddrag
54062          * Fires when a drag operation is complete
54063          * @param {Grid} this
54064          * @param {Roo.GridDD} dd The drag drop object
54065          * @param {event} e The raw browser event
54066          */
54067         "enddrag" : true,
54068         /**
54069          * @event dragdrop
54070          * Fires when dragged row(s) are dropped on a valid DD target
54071          * @param {Grid} this
54072          * @param {Roo.GridDD} dd The drag drop object
54073          * @param {String} targetId The target drag drop object
54074          * @param {event} e The raw browser event
54075          */
54076         "dragdrop" : true,
54077         /**
54078          * @event dragover
54079          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54080          * @param {Grid} this
54081          * @param {Roo.GridDD} dd The drag drop object
54082          * @param {String} targetId The target drag drop object
54083          * @param {event} e The raw browser event
54084          */
54085         "dragover" : true,
54086         /**
54087          * @event dragenter
54088          *  Fires when the dragged row(s) first cross another DD target while being dragged
54089          * @param {Grid} this
54090          * @param {Roo.GridDD} dd The drag drop object
54091          * @param {String} targetId The target drag drop object
54092          * @param {event} e The raw browser event
54093          */
54094         "dragenter" : true,
54095         /**
54096          * @event dragout
54097          * Fires when the dragged row(s) leave another DD target while being dragged
54098          * @param {Grid} this
54099          * @param {Roo.GridDD} dd The drag drop object
54100          * @param {String} targetId The target drag drop object
54101          * @param {event} e The raw browser event
54102          */
54103         "dragout" : true,
54104         /**
54105          * @event rowclass
54106          * Fires when a row is rendered, so you can change add a style to it.
54107          * @param {GridView} gridview   The grid view
54108          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54109          */
54110         'rowclass' : true,
54111
54112         /**
54113          * @event render
54114          * Fires when the grid is rendered
54115          * @param {Grid} grid
54116          */
54117         'render' : true
54118     });
54119
54120     Roo.grid.Grid.superclass.constructor.call(this);
54121 };
54122 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54123     
54124     /**
54125      * @cfg {String} ddGroup - drag drop group.
54126      */
54127
54128     /**
54129      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54130      */
54131     minColumnWidth : 25,
54132
54133     /**
54134      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54135      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54136      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54137      */
54138     autoSizeColumns : false,
54139
54140     /**
54141      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54142      */
54143     autoSizeHeaders : true,
54144
54145     /**
54146      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54147      */
54148     monitorWindowResize : true,
54149
54150     /**
54151      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54152      * rows measured to get a columns size. Default is 0 (all rows).
54153      */
54154     maxRowsToMeasure : 0,
54155
54156     /**
54157      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54158      */
54159     trackMouseOver : true,
54160
54161     /**
54162     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54163     */
54164     
54165     /**
54166     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54167     */
54168     enableDragDrop : false,
54169     
54170     /**
54171     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54172     */
54173     enableColumnMove : true,
54174     
54175     /**
54176     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54177     */
54178     enableColumnHide : true,
54179     
54180     /**
54181     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54182     */
54183     enableRowHeightSync : false,
54184     
54185     /**
54186     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54187     */
54188     stripeRows : true,
54189     
54190     /**
54191     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54192     */
54193     autoHeight : false,
54194
54195     /**
54196      * @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.
54197      */
54198     autoExpandColumn : false,
54199
54200     /**
54201     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54202     * Default is 50.
54203     */
54204     autoExpandMin : 50,
54205
54206     /**
54207     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54208     */
54209     autoExpandMax : 1000,
54210
54211     /**
54212     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54213     */
54214     view : null,
54215
54216     /**
54217     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54218     */
54219     loadMask : false,
54220     /**
54221     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54222     */
54223     dropTarget: false,
54224     
54225    
54226     
54227     // private
54228     rendered : false,
54229
54230     /**
54231     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54232     * of a fixed width. Default is false.
54233     */
54234     /**
54235     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54236     */
54237     /**
54238      * Called once after all setup has been completed and the grid is ready to be rendered.
54239      * @return {Roo.grid.Grid} this
54240      */
54241     render : function()
54242     {
54243         var c = this.container;
54244         // try to detect autoHeight/width mode
54245         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54246             this.autoHeight = true;
54247         }
54248         var view = this.getView();
54249         view.init(this);
54250
54251         c.on("click", this.onClick, this);
54252         c.on("dblclick", this.onDblClick, this);
54253         c.on("contextmenu", this.onContextMenu, this);
54254         c.on("keydown", this.onKeyDown, this);
54255         if (Roo.isTouch) {
54256             c.on("touchstart", this.onTouchStart, this);
54257         }
54258
54259         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54260
54261         this.getSelectionModel().init(this);
54262
54263         view.render();
54264
54265         if(this.loadMask){
54266             this.loadMask = new Roo.LoadMask(this.container,
54267                     Roo.apply({store:this.dataSource}, this.loadMask));
54268         }
54269         
54270         
54271         if (this.toolbar && this.toolbar.xtype) {
54272             this.toolbar.container = this.getView().getHeaderPanel(true);
54273             this.toolbar = new Roo.Toolbar(this.toolbar);
54274         }
54275         if (this.footer && this.footer.xtype) {
54276             this.footer.dataSource = this.getDataSource();
54277             this.footer.container = this.getView().getFooterPanel(true);
54278             this.footer = Roo.factory(this.footer, Roo);
54279         }
54280         if (this.dropTarget && this.dropTarget.xtype) {
54281             delete this.dropTarget.xtype;
54282             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54283         }
54284         
54285         
54286         this.rendered = true;
54287         this.fireEvent('render', this);
54288         return this;
54289     },
54290
54291         /**
54292          * Reconfigures the grid to use a different Store and Column Model.
54293          * The View will be bound to the new objects and refreshed.
54294          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54295          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54296          */
54297     reconfigure : function(dataSource, colModel){
54298         if(this.loadMask){
54299             this.loadMask.destroy();
54300             this.loadMask = new Roo.LoadMask(this.container,
54301                     Roo.apply({store:dataSource}, this.loadMask));
54302         }
54303         this.view.bind(dataSource, colModel);
54304         this.dataSource = dataSource;
54305         this.colModel = colModel;
54306         this.view.refresh(true);
54307     },
54308
54309     // private
54310     onKeyDown : function(e){
54311         this.fireEvent("keydown", e);
54312     },
54313
54314     /**
54315      * Destroy this grid.
54316      * @param {Boolean} removeEl True to remove the element
54317      */
54318     destroy : function(removeEl, keepListeners){
54319         if(this.loadMask){
54320             this.loadMask.destroy();
54321         }
54322         var c = this.container;
54323         c.removeAllListeners();
54324         this.view.destroy();
54325         this.colModel.purgeListeners();
54326         if(!keepListeners){
54327             this.purgeListeners();
54328         }
54329         c.update("");
54330         if(removeEl === true){
54331             c.remove();
54332         }
54333     },
54334
54335     // private
54336     processEvent : function(name, e){
54337         // does this fire select???
54338         //Roo.log('grid:processEvent '  + name);
54339         
54340         if (name != 'touchstart' ) {
54341             this.fireEvent(name, e);    
54342         }
54343         
54344         var t = e.getTarget();
54345         var v = this.view;
54346         var header = v.findHeaderIndex(t);
54347         if(header !== false){
54348             var ename = name == 'touchstart' ? 'click' : name;
54349              
54350             this.fireEvent("header" + ename, this, header, e);
54351         }else{
54352             var row = v.findRowIndex(t);
54353             var cell = v.findCellIndex(t);
54354             if (name == 'touchstart') {
54355                 // first touch is always a click.
54356                 // hopefull this happens after selection is updated.?
54357                 name = false;
54358                 
54359                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54360                     var cs = this.selModel.getSelectedCell();
54361                     if (row == cs[0] && cell == cs[1]){
54362                         name = 'dblclick';
54363                     }
54364                 }
54365                 if (typeof(this.selModel.getSelections) != 'undefined') {
54366                     var cs = this.selModel.getSelections();
54367                     var ds = this.dataSource;
54368                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54369                         name = 'dblclick';
54370                     }
54371                 }
54372                 if (!name) {
54373                     return;
54374                 }
54375             }
54376             
54377             
54378             if(row !== false){
54379                 this.fireEvent("row" + name, this, row, e);
54380                 if(cell !== false){
54381                     this.fireEvent("cell" + name, this, row, cell, e);
54382                 }
54383             }
54384         }
54385     },
54386
54387     // private
54388     onClick : function(e){
54389         this.processEvent("click", e);
54390     },
54391    // private
54392     onTouchStart : function(e){
54393         this.processEvent("touchstart", e);
54394     },
54395
54396     // private
54397     onContextMenu : function(e, t){
54398         this.processEvent("contextmenu", e);
54399     },
54400
54401     // private
54402     onDblClick : function(e){
54403         this.processEvent("dblclick", e);
54404     },
54405
54406     // private
54407     walkCells : function(row, col, step, fn, scope){
54408         var cm = this.colModel, clen = cm.getColumnCount();
54409         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54410         if(step < 0){
54411             if(col < 0){
54412                 row--;
54413                 first = false;
54414             }
54415             while(row >= 0){
54416                 if(!first){
54417                     col = clen-1;
54418                 }
54419                 first = false;
54420                 while(col >= 0){
54421                     if(fn.call(scope || this, row, col, cm) === true){
54422                         return [row, col];
54423                     }
54424                     col--;
54425                 }
54426                 row--;
54427             }
54428         } else {
54429             if(col >= clen){
54430                 row++;
54431                 first = false;
54432             }
54433             while(row < rlen){
54434                 if(!first){
54435                     col = 0;
54436                 }
54437                 first = false;
54438                 while(col < clen){
54439                     if(fn.call(scope || this, row, col, cm) === true){
54440                         return [row, col];
54441                     }
54442                     col++;
54443                 }
54444                 row++;
54445             }
54446         }
54447         return null;
54448     },
54449
54450     // private
54451     getSelections : function(){
54452         return this.selModel.getSelections();
54453     },
54454
54455     /**
54456      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54457      * but if manual update is required this method will initiate it.
54458      */
54459     autoSize : function(){
54460         if(this.rendered){
54461             this.view.layout();
54462             if(this.view.adjustForScroll){
54463                 this.view.adjustForScroll();
54464             }
54465         }
54466     },
54467
54468     /**
54469      * Returns the grid's underlying element.
54470      * @return {Element} The element
54471      */
54472     getGridEl : function(){
54473         return this.container;
54474     },
54475
54476     // private for compatibility, overridden by editor grid
54477     stopEditing : function(){},
54478
54479     /**
54480      * Returns the grid's SelectionModel.
54481      * @return {SelectionModel}
54482      */
54483     getSelectionModel : function(){
54484         if(!this.selModel){
54485             this.selModel = new Roo.grid.RowSelectionModel();
54486         }
54487         return this.selModel;
54488     },
54489
54490     /**
54491      * Returns the grid's DataSource.
54492      * @return {DataSource}
54493      */
54494     getDataSource : function(){
54495         return this.dataSource;
54496     },
54497
54498     /**
54499      * Returns the grid's ColumnModel.
54500      * @return {ColumnModel}
54501      */
54502     getColumnModel : function(){
54503         return this.colModel;
54504     },
54505
54506     /**
54507      * Returns the grid's GridView object.
54508      * @return {GridView}
54509      */
54510     getView : function(){
54511         if(!this.view){
54512             this.view = new Roo.grid.GridView(this.viewConfig);
54513         }
54514         return this.view;
54515     },
54516     /**
54517      * Called to get grid's drag proxy text, by default returns this.ddText.
54518      * @return {String}
54519      */
54520     getDragDropText : function(){
54521         var count = this.selModel.getCount();
54522         return String.format(this.ddText, count, count == 1 ? '' : 's');
54523     }
54524 });
54525 /**
54526  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54527  * %0 is replaced with the number of selected rows.
54528  * @type String
54529  */
54530 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54531  * Based on:
54532  * Ext JS Library 1.1.1
54533  * Copyright(c) 2006-2007, Ext JS, LLC.
54534  *
54535  * Originally Released Under LGPL - original licence link has changed is not relivant.
54536  *
54537  * Fork - LGPL
54538  * <script type="text/javascript">
54539  */
54540  
54541 Roo.grid.AbstractGridView = function(){
54542         this.grid = null;
54543         
54544         this.events = {
54545             "beforerowremoved" : true,
54546             "beforerowsinserted" : true,
54547             "beforerefresh" : true,
54548             "rowremoved" : true,
54549             "rowsinserted" : true,
54550             "rowupdated" : true,
54551             "refresh" : true
54552         };
54553     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54554 };
54555
54556 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54557     rowClass : "x-grid-row",
54558     cellClass : "x-grid-cell",
54559     tdClass : "x-grid-td",
54560     hdClass : "x-grid-hd",
54561     splitClass : "x-grid-hd-split",
54562     
54563     init: function(grid){
54564         this.grid = grid;
54565                 var cid = this.grid.getGridEl().id;
54566         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54567         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54568         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54569         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54570         },
54571         
54572     getColumnRenderers : function(){
54573         var renderers = [];
54574         var cm = this.grid.colModel;
54575         var colCount = cm.getColumnCount();
54576         for(var i = 0; i < colCount; i++){
54577             renderers[i] = cm.getRenderer(i);
54578         }
54579         return renderers;
54580     },
54581     
54582     getColumnIds : function(){
54583         var ids = [];
54584         var cm = this.grid.colModel;
54585         var colCount = cm.getColumnCount();
54586         for(var i = 0; i < colCount; i++){
54587             ids[i] = cm.getColumnId(i);
54588         }
54589         return ids;
54590     },
54591     
54592     getDataIndexes : function(){
54593         if(!this.indexMap){
54594             this.indexMap = this.buildIndexMap();
54595         }
54596         return this.indexMap.colToData;
54597     },
54598     
54599     getColumnIndexByDataIndex : function(dataIndex){
54600         if(!this.indexMap){
54601             this.indexMap = this.buildIndexMap();
54602         }
54603         return this.indexMap.dataToCol[dataIndex];
54604     },
54605     
54606     /**
54607      * Set a css style for a column dynamically. 
54608      * @param {Number} colIndex The index of the column
54609      * @param {String} name The css property name
54610      * @param {String} value The css value
54611      */
54612     setCSSStyle : function(colIndex, name, value){
54613         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54614         Roo.util.CSS.updateRule(selector, name, value);
54615     },
54616     
54617     generateRules : function(cm){
54618         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54619         Roo.util.CSS.removeStyleSheet(rulesId);
54620         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54621             var cid = cm.getColumnId(i);
54622             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54623                          this.tdSelector, cid, " {\n}\n",
54624                          this.hdSelector, cid, " {\n}\n",
54625                          this.splitSelector, cid, " {\n}\n");
54626         }
54627         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54628     }
54629 });/*
54630  * Based on:
54631  * Ext JS Library 1.1.1
54632  * Copyright(c) 2006-2007, Ext JS, LLC.
54633  *
54634  * Originally Released Under LGPL - original licence link has changed is not relivant.
54635  *
54636  * Fork - LGPL
54637  * <script type="text/javascript">
54638  */
54639
54640 // private
54641 // This is a support class used internally by the Grid components
54642 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54643     this.grid = grid;
54644     this.view = grid.getView();
54645     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54646     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54647     if(hd2){
54648         this.setHandleElId(Roo.id(hd));
54649         this.setOuterHandleElId(Roo.id(hd2));
54650     }
54651     this.scroll = false;
54652 };
54653 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54654     maxDragWidth: 120,
54655     getDragData : function(e){
54656         var t = Roo.lib.Event.getTarget(e);
54657         var h = this.view.findHeaderCell(t);
54658         if(h){
54659             return {ddel: h.firstChild, header:h};
54660         }
54661         return false;
54662     },
54663
54664     onInitDrag : function(e){
54665         this.view.headersDisabled = true;
54666         var clone = this.dragData.ddel.cloneNode(true);
54667         clone.id = Roo.id();
54668         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54669         this.proxy.update(clone);
54670         return true;
54671     },
54672
54673     afterValidDrop : function(){
54674         var v = this.view;
54675         setTimeout(function(){
54676             v.headersDisabled = false;
54677         }, 50);
54678     },
54679
54680     afterInvalidDrop : function(){
54681         var v = this.view;
54682         setTimeout(function(){
54683             v.headersDisabled = false;
54684         }, 50);
54685     }
54686 });
54687 /*
54688  * Based on:
54689  * Ext JS Library 1.1.1
54690  * Copyright(c) 2006-2007, Ext JS, LLC.
54691  *
54692  * Originally Released Under LGPL - original licence link has changed is not relivant.
54693  *
54694  * Fork - LGPL
54695  * <script type="text/javascript">
54696  */
54697 // private
54698 // This is a support class used internally by the Grid components
54699 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54700     this.grid = grid;
54701     this.view = grid.getView();
54702     // split the proxies so they don't interfere with mouse events
54703     this.proxyTop = Roo.DomHelper.append(document.body, {
54704         cls:"col-move-top", html:"&#160;"
54705     }, true);
54706     this.proxyBottom = Roo.DomHelper.append(document.body, {
54707         cls:"col-move-bottom", html:"&#160;"
54708     }, true);
54709     this.proxyTop.hide = this.proxyBottom.hide = function(){
54710         this.setLeftTop(-100,-100);
54711         this.setStyle("visibility", "hidden");
54712     };
54713     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54714     // temporarily disabled
54715     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54716     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54717 };
54718 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54719     proxyOffsets : [-4, -9],
54720     fly: Roo.Element.fly,
54721
54722     getTargetFromEvent : function(e){
54723         var t = Roo.lib.Event.getTarget(e);
54724         var cindex = this.view.findCellIndex(t);
54725         if(cindex !== false){
54726             return this.view.getHeaderCell(cindex);
54727         }
54728         return null;
54729     },
54730
54731     nextVisible : function(h){
54732         var v = this.view, cm = this.grid.colModel;
54733         h = h.nextSibling;
54734         while(h){
54735             if(!cm.isHidden(v.getCellIndex(h))){
54736                 return h;
54737             }
54738             h = h.nextSibling;
54739         }
54740         return null;
54741     },
54742
54743     prevVisible : function(h){
54744         var v = this.view, cm = this.grid.colModel;
54745         h = h.prevSibling;
54746         while(h){
54747             if(!cm.isHidden(v.getCellIndex(h))){
54748                 return h;
54749             }
54750             h = h.prevSibling;
54751         }
54752         return null;
54753     },
54754
54755     positionIndicator : function(h, n, e){
54756         var x = Roo.lib.Event.getPageX(e);
54757         var r = Roo.lib.Dom.getRegion(n.firstChild);
54758         var px, pt, py = r.top + this.proxyOffsets[1];
54759         if((r.right - x) <= (r.right-r.left)/2){
54760             px = r.right+this.view.borderWidth;
54761             pt = "after";
54762         }else{
54763             px = r.left;
54764             pt = "before";
54765         }
54766         var oldIndex = this.view.getCellIndex(h);
54767         var newIndex = this.view.getCellIndex(n);
54768
54769         if(this.grid.colModel.isFixed(newIndex)){
54770             return false;
54771         }
54772
54773         var locked = this.grid.colModel.isLocked(newIndex);
54774
54775         if(pt == "after"){
54776             newIndex++;
54777         }
54778         if(oldIndex < newIndex){
54779             newIndex--;
54780         }
54781         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54782             return false;
54783         }
54784         px +=  this.proxyOffsets[0];
54785         this.proxyTop.setLeftTop(px, py);
54786         this.proxyTop.show();
54787         if(!this.bottomOffset){
54788             this.bottomOffset = this.view.mainHd.getHeight();
54789         }
54790         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54791         this.proxyBottom.show();
54792         return pt;
54793     },
54794
54795     onNodeEnter : function(n, dd, e, data){
54796         if(data.header != n){
54797             this.positionIndicator(data.header, n, e);
54798         }
54799     },
54800
54801     onNodeOver : function(n, dd, e, data){
54802         var result = false;
54803         if(data.header != n){
54804             result = this.positionIndicator(data.header, n, e);
54805         }
54806         if(!result){
54807             this.proxyTop.hide();
54808             this.proxyBottom.hide();
54809         }
54810         return result ? this.dropAllowed : this.dropNotAllowed;
54811     },
54812
54813     onNodeOut : function(n, dd, e, data){
54814         this.proxyTop.hide();
54815         this.proxyBottom.hide();
54816     },
54817
54818     onNodeDrop : function(n, dd, e, data){
54819         var h = data.header;
54820         if(h != n){
54821             var cm = this.grid.colModel;
54822             var x = Roo.lib.Event.getPageX(e);
54823             var r = Roo.lib.Dom.getRegion(n.firstChild);
54824             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54825             var oldIndex = this.view.getCellIndex(h);
54826             var newIndex = this.view.getCellIndex(n);
54827             var locked = cm.isLocked(newIndex);
54828             if(pt == "after"){
54829                 newIndex++;
54830             }
54831             if(oldIndex < newIndex){
54832                 newIndex--;
54833             }
54834             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54835                 return false;
54836             }
54837             cm.setLocked(oldIndex, locked, true);
54838             cm.moveColumn(oldIndex, newIndex);
54839             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54840             return true;
54841         }
54842         return false;
54843     }
54844 });
54845 /*
54846  * Based on:
54847  * Ext JS Library 1.1.1
54848  * Copyright(c) 2006-2007, Ext JS, LLC.
54849  *
54850  * Originally Released Under LGPL - original licence link has changed is not relivant.
54851  *
54852  * Fork - LGPL
54853  * <script type="text/javascript">
54854  */
54855   
54856 /**
54857  * @class Roo.grid.GridView
54858  * @extends Roo.util.Observable
54859  *
54860  * @constructor
54861  * @param {Object} config
54862  */
54863 Roo.grid.GridView = function(config){
54864     Roo.grid.GridView.superclass.constructor.call(this);
54865     this.el = null;
54866
54867     Roo.apply(this, config);
54868 };
54869
54870 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
54871
54872     unselectable :  'unselectable="on"',
54873     unselectableCls :  'x-unselectable',
54874     
54875     
54876     rowClass : "x-grid-row",
54877
54878     cellClass : "x-grid-col",
54879
54880     tdClass : "x-grid-td",
54881
54882     hdClass : "x-grid-hd",
54883
54884     splitClass : "x-grid-split",
54885
54886     sortClasses : ["sort-asc", "sort-desc"],
54887
54888     enableMoveAnim : false,
54889
54890     hlColor: "C3DAF9",
54891
54892     dh : Roo.DomHelper,
54893
54894     fly : Roo.Element.fly,
54895
54896     css : Roo.util.CSS,
54897
54898     borderWidth: 1,
54899
54900     splitOffset: 3,
54901
54902     scrollIncrement : 22,
54903
54904     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
54905
54906     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
54907
54908     bind : function(ds, cm){
54909         if(this.ds){
54910             this.ds.un("load", this.onLoad, this);
54911             this.ds.un("datachanged", this.onDataChange, this);
54912             this.ds.un("add", this.onAdd, this);
54913             this.ds.un("remove", this.onRemove, this);
54914             this.ds.un("update", this.onUpdate, this);
54915             this.ds.un("clear", this.onClear, this);
54916         }
54917         if(ds){
54918             ds.on("load", this.onLoad, this);
54919             ds.on("datachanged", this.onDataChange, this);
54920             ds.on("add", this.onAdd, this);
54921             ds.on("remove", this.onRemove, this);
54922             ds.on("update", this.onUpdate, this);
54923             ds.on("clear", this.onClear, this);
54924         }
54925         this.ds = ds;
54926
54927         if(this.cm){
54928             this.cm.un("widthchange", this.onColWidthChange, this);
54929             this.cm.un("headerchange", this.onHeaderChange, this);
54930             this.cm.un("hiddenchange", this.onHiddenChange, this);
54931             this.cm.un("columnmoved", this.onColumnMove, this);
54932             this.cm.un("columnlockchange", this.onColumnLock, this);
54933         }
54934         if(cm){
54935             this.generateRules(cm);
54936             cm.on("widthchange", this.onColWidthChange, this);
54937             cm.on("headerchange", this.onHeaderChange, this);
54938             cm.on("hiddenchange", this.onHiddenChange, this);
54939             cm.on("columnmoved", this.onColumnMove, this);
54940             cm.on("columnlockchange", this.onColumnLock, this);
54941         }
54942         this.cm = cm;
54943     },
54944
54945     init: function(grid){
54946         Roo.grid.GridView.superclass.init.call(this, grid);
54947
54948         this.bind(grid.dataSource, grid.colModel);
54949
54950         grid.on("headerclick", this.handleHeaderClick, this);
54951
54952         if(grid.trackMouseOver){
54953             grid.on("mouseover", this.onRowOver, this);
54954             grid.on("mouseout", this.onRowOut, this);
54955         }
54956         grid.cancelTextSelection = function(){};
54957         this.gridId = grid.id;
54958
54959         var tpls = this.templates || {};
54960
54961         if(!tpls.master){
54962             tpls.master = new Roo.Template(
54963                '<div class="x-grid" hidefocus="true">',
54964                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
54965                   '<div class="x-grid-topbar"></div>',
54966                   '<div class="x-grid-scroller"><div></div></div>',
54967                   '<div class="x-grid-locked">',
54968                       '<div class="x-grid-header">{lockedHeader}</div>',
54969                       '<div class="x-grid-body">{lockedBody}</div>',
54970                   "</div>",
54971                   '<div class="x-grid-viewport">',
54972                       '<div class="x-grid-header">{header}</div>',
54973                       '<div class="x-grid-body">{body}</div>',
54974                   "</div>",
54975                   '<div class="x-grid-bottombar"></div>',
54976                  
54977                   '<div class="x-grid-resize-proxy">&#160;</div>',
54978                "</div>"
54979             );
54980             tpls.master.disableformats = true;
54981         }
54982
54983         if(!tpls.header){
54984             tpls.header = new Roo.Template(
54985                '<table border="0" cellspacing="0" cellpadding="0">',
54986                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
54987                "</table>{splits}"
54988             );
54989             tpls.header.disableformats = true;
54990         }
54991         tpls.header.compile();
54992
54993         if(!tpls.hcell){
54994             tpls.hcell = new Roo.Template(
54995                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
54996                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
54997                 "</div></td>"
54998              );
54999              tpls.hcell.disableFormats = true;
55000         }
55001         tpls.hcell.compile();
55002
55003         if(!tpls.hsplit){
55004             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55005                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55006             tpls.hsplit.disableFormats = true;
55007         }
55008         tpls.hsplit.compile();
55009
55010         if(!tpls.body){
55011             tpls.body = new Roo.Template(
55012                '<table border="0" cellspacing="0" cellpadding="0">',
55013                "<tbody>{rows}</tbody>",
55014                "</table>"
55015             );
55016             tpls.body.disableFormats = true;
55017         }
55018         tpls.body.compile();
55019
55020         if(!tpls.row){
55021             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55022             tpls.row.disableFormats = true;
55023         }
55024         tpls.row.compile();
55025
55026         if(!tpls.cell){
55027             tpls.cell = new Roo.Template(
55028                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55029                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55030                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55031                 "</td>"
55032             );
55033             tpls.cell.disableFormats = true;
55034         }
55035         tpls.cell.compile();
55036
55037         this.templates = tpls;
55038     },
55039
55040     // remap these for backwards compat
55041     onColWidthChange : function(){
55042         this.updateColumns.apply(this, arguments);
55043     },
55044     onHeaderChange : function(){
55045         this.updateHeaders.apply(this, arguments);
55046     }, 
55047     onHiddenChange : function(){
55048         this.handleHiddenChange.apply(this, arguments);
55049     },
55050     onColumnMove : function(){
55051         this.handleColumnMove.apply(this, arguments);
55052     },
55053     onColumnLock : function(){
55054         this.handleLockChange.apply(this, arguments);
55055     },
55056
55057     onDataChange : function(){
55058         this.refresh();
55059         this.updateHeaderSortState();
55060     },
55061
55062     onClear : function(){
55063         this.refresh();
55064     },
55065
55066     onUpdate : function(ds, record){
55067         this.refreshRow(record);
55068     },
55069
55070     refreshRow : function(record){
55071         var ds = this.ds, index;
55072         if(typeof record == 'number'){
55073             index = record;
55074             record = ds.getAt(index);
55075         }else{
55076             index = ds.indexOf(record);
55077         }
55078         this.insertRows(ds, index, index, true);
55079         this.onRemove(ds, record, index+1, true);
55080         this.syncRowHeights(index, index);
55081         this.layout();
55082         this.fireEvent("rowupdated", this, index, record);
55083     },
55084
55085     onAdd : function(ds, records, index){
55086         this.insertRows(ds, index, index + (records.length-1));
55087     },
55088
55089     onRemove : function(ds, record, index, isUpdate){
55090         if(isUpdate !== true){
55091             this.fireEvent("beforerowremoved", this, index, record);
55092         }
55093         var bt = this.getBodyTable(), lt = this.getLockedTable();
55094         if(bt.rows[index]){
55095             bt.firstChild.removeChild(bt.rows[index]);
55096         }
55097         if(lt.rows[index]){
55098             lt.firstChild.removeChild(lt.rows[index]);
55099         }
55100         if(isUpdate !== true){
55101             this.stripeRows(index);
55102             this.syncRowHeights(index, index);
55103             this.layout();
55104             this.fireEvent("rowremoved", this, index, record);
55105         }
55106     },
55107
55108     onLoad : function(){
55109         this.scrollToTop();
55110     },
55111
55112     /**
55113      * Scrolls the grid to the top
55114      */
55115     scrollToTop : function(){
55116         if(this.scroller){
55117             this.scroller.dom.scrollTop = 0;
55118             this.syncScroll();
55119         }
55120     },
55121
55122     /**
55123      * Gets a panel in the header of the grid that can be used for toolbars etc.
55124      * After modifying the contents of this panel a call to grid.autoSize() may be
55125      * required to register any changes in size.
55126      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55127      * @return Roo.Element
55128      */
55129     getHeaderPanel : function(doShow){
55130         if(doShow){
55131             this.headerPanel.show();
55132         }
55133         return this.headerPanel;
55134     },
55135
55136     /**
55137      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55138      * After modifying the contents of this panel a call to grid.autoSize() may be
55139      * required to register any changes in size.
55140      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55141      * @return Roo.Element
55142      */
55143     getFooterPanel : function(doShow){
55144         if(doShow){
55145             this.footerPanel.show();
55146         }
55147         return this.footerPanel;
55148     },
55149
55150     initElements : function(){
55151         var E = Roo.Element;
55152         var el = this.grid.getGridEl().dom.firstChild;
55153         var cs = el.childNodes;
55154
55155         this.el = new E(el);
55156         
55157          this.focusEl = new E(el.firstChild);
55158         this.focusEl.swallowEvent("click", true);
55159         
55160         this.headerPanel = new E(cs[1]);
55161         this.headerPanel.enableDisplayMode("block");
55162
55163         this.scroller = new E(cs[2]);
55164         this.scrollSizer = new E(this.scroller.dom.firstChild);
55165
55166         this.lockedWrap = new E(cs[3]);
55167         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55168         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55169
55170         this.mainWrap = new E(cs[4]);
55171         this.mainHd = new E(this.mainWrap.dom.firstChild);
55172         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55173
55174         this.footerPanel = new E(cs[5]);
55175         this.footerPanel.enableDisplayMode("block");
55176
55177         this.resizeProxy = new E(cs[6]);
55178
55179         this.headerSelector = String.format(
55180            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55181            this.lockedHd.id, this.mainHd.id
55182         );
55183
55184         this.splitterSelector = String.format(
55185            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55186            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55187         );
55188     },
55189     idToCssName : function(s)
55190     {
55191         return s.replace(/[^a-z0-9]+/ig, '-');
55192     },
55193
55194     getHeaderCell : function(index){
55195         return Roo.DomQuery.select(this.headerSelector)[index];
55196     },
55197
55198     getHeaderCellMeasure : function(index){
55199         return this.getHeaderCell(index).firstChild;
55200     },
55201
55202     getHeaderCellText : function(index){
55203         return this.getHeaderCell(index).firstChild.firstChild;
55204     },
55205
55206     getLockedTable : function(){
55207         return this.lockedBody.dom.firstChild;
55208     },
55209
55210     getBodyTable : function(){
55211         return this.mainBody.dom.firstChild;
55212     },
55213
55214     getLockedRow : function(index){
55215         return this.getLockedTable().rows[index];
55216     },
55217
55218     getRow : function(index){
55219         return this.getBodyTable().rows[index];
55220     },
55221
55222     getRowComposite : function(index){
55223         if(!this.rowEl){
55224             this.rowEl = new Roo.CompositeElementLite();
55225         }
55226         var els = [], lrow, mrow;
55227         if(lrow = this.getLockedRow(index)){
55228             els.push(lrow);
55229         }
55230         if(mrow = this.getRow(index)){
55231             els.push(mrow);
55232         }
55233         this.rowEl.elements = els;
55234         return this.rowEl;
55235     },
55236     /**
55237      * Gets the 'td' of the cell
55238      * 
55239      * @param {Integer} rowIndex row to select
55240      * @param {Integer} colIndex column to select
55241      * 
55242      * @return {Object} 
55243      */
55244     getCell : function(rowIndex, colIndex){
55245         var locked = this.cm.getLockedCount();
55246         var source;
55247         if(colIndex < locked){
55248             source = this.lockedBody.dom.firstChild;
55249         }else{
55250             source = this.mainBody.dom.firstChild;
55251             colIndex -= locked;
55252         }
55253         return source.rows[rowIndex].childNodes[colIndex];
55254     },
55255
55256     getCellText : function(rowIndex, colIndex){
55257         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55258     },
55259
55260     getCellBox : function(cell){
55261         var b = this.fly(cell).getBox();
55262         if(Roo.isOpera){ // opera fails to report the Y
55263             b.y = cell.offsetTop + this.mainBody.getY();
55264         }
55265         return b;
55266     },
55267
55268     getCellIndex : function(cell){
55269         var id = String(cell.className).match(this.cellRE);
55270         if(id){
55271             return parseInt(id[1], 10);
55272         }
55273         return 0;
55274     },
55275
55276     findHeaderIndex : function(n){
55277         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55278         return r ? this.getCellIndex(r) : false;
55279     },
55280
55281     findHeaderCell : function(n){
55282         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55283         return r ? r : false;
55284     },
55285
55286     findRowIndex : function(n){
55287         if(!n){
55288             return false;
55289         }
55290         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55291         return r ? r.rowIndex : false;
55292     },
55293
55294     findCellIndex : function(node){
55295         var stop = this.el.dom;
55296         while(node && node != stop){
55297             if(this.findRE.test(node.className)){
55298                 return this.getCellIndex(node);
55299             }
55300             node = node.parentNode;
55301         }
55302         return false;
55303     },
55304
55305     getColumnId : function(index){
55306         return this.cm.getColumnId(index);
55307     },
55308
55309     getSplitters : function()
55310     {
55311         if(this.splitterSelector){
55312            return Roo.DomQuery.select(this.splitterSelector);
55313         }else{
55314             return null;
55315       }
55316     },
55317
55318     getSplitter : function(index){
55319         return this.getSplitters()[index];
55320     },
55321
55322     onRowOver : function(e, t){
55323         var row;
55324         if((row = this.findRowIndex(t)) !== false){
55325             this.getRowComposite(row).addClass("x-grid-row-over");
55326         }
55327     },
55328
55329     onRowOut : function(e, t){
55330         var row;
55331         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55332             this.getRowComposite(row).removeClass("x-grid-row-over");
55333         }
55334     },
55335
55336     renderHeaders : function(){
55337         var cm = this.cm;
55338         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55339         var cb = [], lb = [], sb = [], lsb = [], p = {};
55340         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55341             p.cellId = "x-grid-hd-0-" + i;
55342             p.splitId = "x-grid-csplit-0-" + i;
55343             p.id = cm.getColumnId(i);
55344             p.value = cm.getColumnHeader(i) || "";
55345             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55346             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55347             if(!cm.isLocked(i)){
55348                 cb[cb.length] = ct.apply(p);
55349                 sb[sb.length] = st.apply(p);
55350             }else{
55351                 lb[lb.length] = ct.apply(p);
55352                 lsb[lsb.length] = st.apply(p);
55353             }
55354         }
55355         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55356                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55357     },
55358
55359     updateHeaders : function(){
55360         var html = this.renderHeaders();
55361         this.lockedHd.update(html[0]);
55362         this.mainHd.update(html[1]);
55363     },
55364
55365     /**
55366      * Focuses the specified row.
55367      * @param {Number} row The row index
55368      */
55369     focusRow : function(row)
55370     {
55371         //Roo.log('GridView.focusRow');
55372         var x = this.scroller.dom.scrollLeft;
55373         this.focusCell(row, 0, false);
55374         this.scroller.dom.scrollLeft = x;
55375     },
55376
55377     /**
55378      * Focuses the specified cell.
55379      * @param {Number} row The row index
55380      * @param {Number} col The column index
55381      * @param {Boolean} hscroll false to disable horizontal scrolling
55382      */
55383     focusCell : function(row, col, hscroll)
55384     {
55385         //Roo.log('GridView.focusCell');
55386         var el = this.ensureVisible(row, col, hscroll);
55387         this.focusEl.alignTo(el, "tl-tl");
55388         if(Roo.isGecko){
55389             this.focusEl.focus();
55390         }else{
55391             this.focusEl.focus.defer(1, this.focusEl);
55392         }
55393     },
55394
55395     /**
55396      * Scrolls the specified cell into view
55397      * @param {Number} row The row index
55398      * @param {Number} col The column index
55399      * @param {Boolean} hscroll false to disable horizontal scrolling
55400      */
55401     ensureVisible : function(row, col, hscroll)
55402     {
55403         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55404         //return null; //disable for testing.
55405         if(typeof row != "number"){
55406             row = row.rowIndex;
55407         }
55408         if(row < 0 && row >= this.ds.getCount()){
55409             return  null;
55410         }
55411         col = (col !== undefined ? col : 0);
55412         var cm = this.grid.colModel;
55413         while(cm.isHidden(col)){
55414             col++;
55415         }
55416
55417         var el = this.getCell(row, col);
55418         if(!el){
55419             return null;
55420         }
55421         var c = this.scroller.dom;
55422
55423         var ctop = parseInt(el.offsetTop, 10);
55424         var cleft = parseInt(el.offsetLeft, 10);
55425         var cbot = ctop + el.offsetHeight;
55426         var cright = cleft + el.offsetWidth;
55427         
55428         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55429         var stop = parseInt(c.scrollTop, 10);
55430         var sleft = parseInt(c.scrollLeft, 10);
55431         var sbot = stop + ch;
55432         var sright = sleft + c.clientWidth;
55433         /*
55434         Roo.log('GridView.ensureVisible:' +
55435                 ' ctop:' + ctop +
55436                 ' c.clientHeight:' + c.clientHeight +
55437                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55438                 ' stop:' + stop +
55439                 ' cbot:' + cbot +
55440                 ' sbot:' + sbot +
55441                 ' ch:' + ch  
55442                 );
55443         */
55444         if(ctop < stop){
55445              c.scrollTop = ctop;
55446             //Roo.log("set scrolltop to ctop DISABLE?");
55447         }else if(cbot > sbot){
55448             //Roo.log("set scrolltop to cbot-ch");
55449             c.scrollTop = cbot-ch;
55450         }
55451         
55452         if(hscroll !== false){
55453             if(cleft < sleft){
55454                 c.scrollLeft = cleft;
55455             }else if(cright > sright){
55456                 c.scrollLeft = cright-c.clientWidth;
55457             }
55458         }
55459          
55460         return el;
55461     },
55462
55463     updateColumns : function(){
55464         this.grid.stopEditing();
55465         var cm = this.grid.colModel, colIds = this.getColumnIds();
55466         //var totalWidth = cm.getTotalWidth();
55467         var pos = 0;
55468         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55469             //if(cm.isHidden(i)) continue;
55470             var w = cm.getColumnWidth(i);
55471             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55472             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55473         }
55474         this.updateSplitters();
55475     },
55476
55477     generateRules : function(cm){
55478         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55479         Roo.util.CSS.removeStyleSheet(rulesId);
55480         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55481             var cid = cm.getColumnId(i);
55482             var align = '';
55483             if(cm.config[i].align){
55484                 align = 'text-align:'+cm.config[i].align+';';
55485             }
55486             var hidden = '';
55487             if(cm.isHidden(i)){
55488                 hidden = 'display:none;';
55489             }
55490             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55491             ruleBuf.push(
55492                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55493                     this.hdSelector, cid, " {\n", align, width, "}\n",
55494                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55495                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55496         }
55497         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55498     },
55499
55500     updateSplitters : function(){
55501         var cm = this.cm, s = this.getSplitters();
55502         if(s){ // splitters not created yet
55503             var pos = 0, locked = true;
55504             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55505                 if(cm.isHidden(i)) {
55506                     continue;
55507                 }
55508                 var w = cm.getColumnWidth(i); // make sure it's a number
55509                 if(!cm.isLocked(i) && locked){
55510                     pos = 0;
55511                     locked = false;
55512                 }
55513                 pos += w;
55514                 s[i].style.left = (pos-this.splitOffset) + "px";
55515             }
55516         }
55517     },
55518
55519     handleHiddenChange : function(colModel, colIndex, hidden){
55520         if(hidden){
55521             this.hideColumn(colIndex);
55522         }else{
55523             this.unhideColumn(colIndex);
55524         }
55525     },
55526
55527     hideColumn : function(colIndex){
55528         var cid = this.getColumnId(colIndex);
55529         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55530         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55531         if(Roo.isSafari){
55532             this.updateHeaders();
55533         }
55534         this.updateSplitters();
55535         this.layout();
55536     },
55537
55538     unhideColumn : function(colIndex){
55539         var cid = this.getColumnId(colIndex);
55540         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55541         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55542
55543         if(Roo.isSafari){
55544             this.updateHeaders();
55545         }
55546         this.updateSplitters();
55547         this.layout();
55548     },
55549
55550     insertRows : function(dm, firstRow, lastRow, isUpdate){
55551         if(firstRow == 0 && lastRow == dm.getCount()-1){
55552             this.refresh();
55553         }else{
55554             if(!isUpdate){
55555                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55556             }
55557             var s = this.getScrollState();
55558             var markup = this.renderRows(firstRow, lastRow);
55559             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55560             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55561             this.restoreScroll(s);
55562             if(!isUpdate){
55563                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55564                 this.syncRowHeights(firstRow, lastRow);
55565                 this.stripeRows(firstRow);
55566                 this.layout();
55567             }
55568         }
55569     },
55570
55571     bufferRows : function(markup, target, index){
55572         var before = null, trows = target.rows, tbody = target.tBodies[0];
55573         if(index < trows.length){
55574             before = trows[index];
55575         }
55576         var b = document.createElement("div");
55577         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55578         var rows = b.firstChild.rows;
55579         for(var i = 0, len = rows.length; i < len; i++){
55580             if(before){
55581                 tbody.insertBefore(rows[0], before);
55582             }else{
55583                 tbody.appendChild(rows[0]);
55584             }
55585         }
55586         b.innerHTML = "";
55587         b = null;
55588     },
55589
55590     deleteRows : function(dm, firstRow, lastRow){
55591         if(dm.getRowCount()<1){
55592             this.fireEvent("beforerefresh", this);
55593             this.mainBody.update("");
55594             this.lockedBody.update("");
55595             this.fireEvent("refresh", this);
55596         }else{
55597             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55598             var bt = this.getBodyTable();
55599             var tbody = bt.firstChild;
55600             var rows = bt.rows;
55601             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55602                 tbody.removeChild(rows[firstRow]);
55603             }
55604             this.stripeRows(firstRow);
55605             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55606         }
55607     },
55608
55609     updateRows : function(dataSource, firstRow, lastRow){
55610         var s = this.getScrollState();
55611         this.refresh();
55612         this.restoreScroll(s);
55613     },
55614
55615     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55616         if(!noRefresh){
55617            this.refresh();
55618         }
55619         this.updateHeaderSortState();
55620     },
55621
55622     getScrollState : function(){
55623         
55624         var sb = this.scroller.dom;
55625         return {left: sb.scrollLeft, top: sb.scrollTop};
55626     },
55627
55628     stripeRows : function(startRow){
55629         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55630             return;
55631         }
55632         startRow = startRow || 0;
55633         var rows = this.getBodyTable().rows;
55634         var lrows = this.getLockedTable().rows;
55635         var cls = ' x-grid-row-alt ';
55636         for(var i = startRow, len = rows.length; i < len; i++){
55637             var row = rows[i], lrow = lrows[i];
55638             var isAlt = ((i+1) % 2 == 0);
55639             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55640             if(isAlt == hasAlt){
55641                 continue;
55642             }
55643             if(isAlt){
55644                 row.className += " x-grid-row-alt";
55645             }else{
55646                 row.className = row.className.replace("x-grid-row-alt", "");
55647             }
55648             if(lrow){
55649                 lrow.className = row.className;
55650             }
55651         }
55652     },
55653
55654     restoreScroll : function(state){
55655         //Roo.log('GridView.restoreScroll');
55656         var sb = this.scroller.dom;
55657         sb.scrollLeft = state.left;
55658         sb.scrollTop = state.top;
55659         this.syncScroll();
55660     },
55661
55662     syncScroll : function(){
55663         //Roo.log('GridView.syncScroll');
55664         var sb = this.scroller.dom;
55665         var sh = this.mainHd.dom;
55666         var bs = this.mainBody.dom;
55667         var lv = this.lockedBody.dom;
55668         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55669         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55670     },
55671
55672     handleScroll : function(e){
55673         this.syncScroll();
55674         var sb = this.scroller.dom;
55675         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55676         e.stopEvent();
55677     },
55678
55679     handleWheel : function(e){
55680         var d = e.getWheelDelta();
55681         this.scroller.dom.scrollTop -= d*22;
55682         // set this here to prevent jumpy scrolling on large tables
55683         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55684         e.stopEvent();
55685     },
55686
55687     renderRows : function(startRow, endRow){
55688         // pull in all the crap needed to render rows
55689         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55690         var colCount = cm.getColumnCount();
55691
55692         if(ds.getCount() < 1){
55693             return ["", ""];
55694         }
55695
55696         // build a map for all the columns
55697         var cs = [];
55698         for(var i = 0; i < colCount; i++){
55699             var name = cm.getDataIndex(i);
55700             cs[i] = {
55701                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55702                 renderer : cm.getRenderer(i),
55703                 id : cm.getColumnId(i),
55704                 locked : cm.isLocked(i),
55705                 has_editor : cm.isCellEditable(i)
55706             };
55707         }
55708
55709         startRow = startRow || 0;
55710         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55711
55712         // records to render
55713         var rs = ds.getRange(startRow, endRow);
55714
55715         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55716     },
55717
55718     // As much as I hate to duplicate code, this was branched because FireFox really hates
55719     // [].join("") on strings. The performance difference was substantial enough to
55720     // branch this function
55721     doRender : Roo.isGecko ?
55722             function(cs, rs, ds, startRow, colCount, stripe){
55723                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55724                 // buffers
55725                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55726                 
55727                 var hasListener = this.grid.hasListener('rowclass');
55728                 var rowcfg = {};
55729                 for(var j = 0, len = rs.length; j < len; j++){
55730                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55731                     for(var i = 0; i < colCount; i++){
55732                         c = cs[i];
55733                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55734                         p.id = c.id;
55735                         p.css = p.attr = "";
55736                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55737                         if(p.value == undefined || p.value === "") {
55738                             p.value = "&#160;";
55739                         }
55740                         if(c.has_editor){
55741                             p.css += ' x-grid-editable-cell';
55742                         }
55743                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55744                             p.css +=  ' x-grid-dirty-cell';
55745                         }
55746                         var markup = ct.apply(p);
55747                         if(!c.locked){
55748                             cb+= markup;
55749                         }else{
55750                             lcb+= markup;
55751                         }
55752                     }
55753                     var alt = [];
55754                     if(stripe && ((rowIndex+1) % 2 == 0)){
55755                         alt.push("x-grid-row-alt")
55756                     }
55757                     if(r.dirty){
55758                         alt.push(  " x-grid-dirty-row");
55759                     }
55760                     rp.cells = lcb;
55761                     if(this.getRowClass){
55762                         alt.push(this.getRowClass(r, rowIndex));
55763                     }
55764                     if (hasListener) {
55765                         rowcfg = {
55766                              
55767                             record: r,
55768                             rowIndex : rowIndex,
55769                             rowClass : ''
55770                         };
55771                         this.grid.fireEvent('rowclass', this, rowcfg);
55772                         alt.push(rowcfg.rowClass);
55773                     }
55774                     rp.alt = alt.join(" ");
55775                     lbuf+= rt.apply(rp);
55776                     rp.cells = cb;
55777                     buf+=  rt.apply(rp);
55778                 }
55779                 return [lbuf, buf];
55780             } :
55781             function(cs, rs, ds, startRow, colCount, stripe){
55782                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55783                 // buffers
55784                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55785                 var hasListener = this.grid.hasListener('rowclass');
55786  
55787                 var rowcfg = {};
55788                 for(var j = 0, len = rs.length; j < len; j++){
55789                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55790                     for(var i = 0; i < colCount; i++){
55791                         c = cs[i];
55792                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55793                         p.id = c.id;
55794                         p.css = p.attr = "";
55795                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55796                         if(p.value == undefined || p.value === "") {
55797                             p.value = "&#160;";
55798                         }
55799                         //Roo.log(c);
55800                          if(c.has_editor){
55801                             p.css += ' x-grid-editable-cell';
55802                         }
55803                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55804                             p.css += ' x-grid-dirty-cell' 
55805                         }
55806                         
55807                         var markup = ct.apply(p);
55808                         if(!c.locked){
55809                             cb[cb.length] = markup;
55810                         }else{
55811                             lcb[lcb.length] = markup;
55812                         }
55813                     }
55814                     var alt = [];
55815                     if(stripe && ((rowIndex+1) % 2 == 0)){
55816                         alt.push( "x-grid-row-alt");
55817                     }
55818                     if(r.dirty){
55819                         alt.push(" x-grid-dirty-row");
55820                     }
55821                     rp.cells = lcb;
55822                     if(this.getRowClass){
55823                         alt.push( this.getRowClass(r, rowIndex));
55824                     }
55825                     if (hasListener) {
55826                         rowcfg = {
55827                              
55828                             record: r,
55829                             rowIndex : rowIndex,
55830                             rowClass : ''
55831                         };
55832                         this.grid.fireEvent('rowclass', this, rowcfg);
55833                         alt.push(rowcfg.rowClass);
55834                     }
55835                     
55836                     rp.alt = alt.join(" ");
55837                     rp.cells = lcb.join("");
55838                     lbuf[lbuf.length] = rt.apply(rp);
55839                     rp.cells = cb.join("");
55840                     buf[buf.length] =  rt.apply(rp);
55841                 }
55842                 return [lbuf.join(""), buf.join("")];
55843             },
55844
55845     renderBody : function(){
55846         var markup = this.renderRows();
55847         var bt = this.templates.body;
55848         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
55849     },
55850
55851     /**
55852      * Refreshes the grid
55853      * @param {Boolean} headersToo
55854      */
55855     refresh : function(headersToo){
55856         this.fireEvent("beforerefresh", this);
55857         this.grid.stopEditing();
55858         var result = this.renderBody();
55859         this.lockedBody.update(result[0]);
55860         this.mainBody.update(result[1]);
55861         if(headersToo === true){
55862             this.updateHeaders();
55863             this.updateColumns();
55864             this.updateSplitters();
55865             this.updateHeaderSortState();
55866         }
55867         this.syncRowHeights();
55868         this.layout();
55869         this.fireEvent("refresh", this);
55870     },
55871
55872     handleColumnMove : function(cm, oldIndex, newIndex){
55873         this.indexMap = null;
55874         var s = this.getScrollState();
55875         this.refresh(true);
55876         this.restoreScroll(s);
55877         this.afterMove(newIndex);
55878     },
55879
55880     afterMove : function(colIndex){
55881         if(this.enableMoveAnim && Roo.enableFx){
55882             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
55883         }
55884         // if multisort - fix sortOrder, and reload..
55885         if (this.grid.dataSource.multiSort) {
55886             // the we can call sort again..
55887             var dm = this.grid.dataSource;
55888             var cm = this.grid.colModel;
55889             var so = [];
55890             for(var i = 0; i < cm.config.length; i++ ) {
55891                 
55892                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
55893                     continue; // dont' bother, it's not in sort list or being set.
55894                 }
55895                 
55896                 so.push(cm.config[i].dataIndex);
55897             };
55898             dm.sortOrder = so;
55899             dm.load(dm.lastOptions);
55900             
55901             
55902         }
55903         
55904     },
55905
55906     updateCell : function(dm, rowIndex, dataIndex){
55907         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
55908         if(typeof colIndex == "undefined"){ // not present in grid
55909             return;
55910         }
55911         var cm = this.grid.colModel;
55912         var cell = this.getCell(rowIndex, colIndex);
55913         var cellText = this.getCellText(rowIndex, colIndex);
55914
55915         var p = {
55916             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
55917             id : cm.getColumnId(colIndex),
55918             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
55919         };
55920         var renderer = cm.getRenderer(colIndex);
55921         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
55922         if(typeof val == "undefined" || val === "") {
55923             val = "&#160;";
55924         }
55925         cellText.innerHTML = val;
55926         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
55927         this.syncRowHeights(rowIndex, rowIndex);
55928     },
55929
55930     calcColumnWidth : function(colIndex, maxRowsToMeasure){
55931         var maxWidth = 0;
55932         if(this.grid.autoSizeHeaders){
55933             var h = this.getHeaderCellMeasure(colIndex);
55934             maxWidth = Math.max(maxWidth, h.scrollWidth);
55935         }
55936         var tb, index;
55937         if(this.cm.isLocked(colIndex)){
55938             tb = this.getLockedTable();
55939             index = colIndex;
55940         }else{
55941             tb = this.getBodyTable();
55942             index = colIndex - this.cm.getLockedCount();
55943         }
55944         if(tb && tb.rows){
55945             var rows = tb.rows;
55946             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
55947             for(var i = 0; i < stopIndex; i++){
55948                 var cell = rows[i].childNodes[index].firstChild;
55949                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
55950             }
55951         }
55952         return maxWidth + /*margin for error in IE*/ 5;
55953     },
55954     /**
55955      * Autofit a column to its content.
55956      * @param {Number} colIndex
55957      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
55958      */
55959      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
55960          if(this.cm.isHidden(colIndex)){
55961              return; // can't calc a hidden column
55962          }
55963         if(forceMinSize){
55964             var cid = this.cm.getColumnId(colIndex);
55965             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
55966            if(this.grid.autoSizeHeaders){
55967                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
55968            }
55969         }
55970         var newWidth = this.calcColumnWidth(colIndex);
55971         this.cm.setColumnWidth(colIndex,
55972             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
55973         if(!suppressEvent){
55974             this.grid.fireEvent("columnresize", colIndex, newWidth);
55975         }
55976     },
55977
55978     /**
55979      * Autofits all columns to their content and then expands to fit any extra space in the grid
55980      */
55981      autoSizeColumns : function(){
55982         var cm = this.grid.colModel;
55983         var colCount = cm.getColumnCount();
55984         for(var i = 0; i < colCount; i++){
55985             this.autoSizeColumn(i, true, true);
55986         }
55987         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
55988             this.fitColumns();
55989         }else{
55990             this.updateColumns();
55991             this.layout();
55992         }
55993     },
55994
55995     /**
55996      * Autofits all columns to the grid's width proportionate with their current size
55997      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
55998      */
55999     fitColumns : function(reserveScrollSpace){
56000         var cm = this.grid.colModel;
56001         var colCount = cm.getColumnCount();
56002         var cols = [];
56003         var width = 0;
56004         var i, w;
56005         for (i = 0; i < colCount; i++){
56006             if(!cm.isHidden(i) && !cm.isFixed(i)){
56007                 w = cm.getColumnWidth(i);
56008                 cols.push(i);
56009                 cols.push(w);
56010                 width += w;
56011             }
56012         }
56013         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56014         if(reserveScrollSpace){
56015             avail -= 17;
56016         }
56017         var frac = (avail - cm.getTotalWidth())/width;
56018         while (cols.length){
56019             w = cols.pop();
56020             i = cols.pop();
56021             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56022         }
56023         this.updateColumns();
56024         this.layout();
56025     },
56026
56027     onRowSelect : function(rowIndex){
56028         var row = this.getRowComposite(rowIndex);
56029         row.addClass("x-grid-row-selected");
56030     },
56031
56032     onRowDeselect : function(rowIndex){
56033         var row = this.getRowComposite(rowIndex);
56034         row.removeClass("x-grid-row-selected");
56035     },
56036
56037     onCellSelect : function(row, col){
56038         var cell = this.getCell(row, col);
56039         if(cell){
56040             Roo.fly(cell).addClass("x-grid-cell-selected");
56041         }
56042     },
56043
56044     onCellDeselect : function(row, col){
56045         var cell = this.getCell(row, col);
56046         if(cell){
56047             Roo.fly(cell).removeClass("x-grid-cell-selected");
56048         }
56049     },
56050
56051     updateHeaderSortState : function(){
56052         
56053         // sort state can be single { field: xxx, direction : yyy}
56054         // or   { xxx=>ASC , yyy : DESC ..... }
56055         
56056         var mstate = {};
56057         if (!this.ds.multiSort) { 
56058             var state = this.ds.getSortState();
56059             if(!state){
56060                 return;
56061             }
56062             mstate[state.field] = state.direction;
56063             // FIXME... - this is not used here.. but might be elsewhere..
56064             this.sortState = state;
56065             
56066         } else {
56067             mstate = this.ds.sortToggle;
56068         }
56069         //remove existing sort classes..
56070         
56071         var sc = this.sortClasses;
56072         var hds = this.el.select(this.headerSelector).removeClass(sc);
56073         
56074         for(var f in mstate) {
56075         
56076             var sortColumn = this.cm.findColumnIndex(f);
56077             
56078             if(sortColumn != -1){
56079                 var sortDir = mstate[f];        
56080                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56081             }
56082         }
56083         
56084          
56085         
56086     },
56087
56088
56089     handleHeaderClick : function(g, index,e){
56090         
56091         Roo.log("header click");
56092         
56093         if (Roo.isTouch) {
56094             // touch events on header are handled by context
56095             this.handleHdCtx(g,index,e);
56096             return;
56097         }
56098         
56099         
56100         if(this.headersDisabled){
56101             return;
56102         }
56103         var dm = g.dataSource, cm = g.colModel;
56104         if(!cm.isSortable(index)){
56105             return;
56106         }
56107         g.stopEditing();
56108         
56109         if (dm.multiSort) {
56110             // update the sortOrder
56111             var so = [];
56112             for(var i = 0; i < cm.config.length; i++ ) {
56113                 
56114                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56115                     continue; // dont' bother, it's not in sort list or being set.
56116                 }
56117                 
56118                 so.push(cm.config[i].dataIndex);
56119             };
56120             dm.sortOrder = so;
56121         }
56122         
56123         
56124         dm.sort(cm.getDataIndex(index));
56125     },
56126
56127
56128     destroy : function(){
56129         if(this.colMenu){
56130             this.colMenu.removeAll();
56131             Roo.menu.MenuMgr.unregister(this.colMenu);
56132             this.colMenu.getEl().remove();
56133             delete this.colMenu;
56134         }
56135         if(this.hmenu){
56136             this.hmenu.removeAll();
56137             Roo.menu.MenuMgr.unregister(this.hmenu);
56138             this.hmenu.getEl().remove();
56139             delete this.hmenu;
56140         }
56141         if(this.grid.enableColumnMove){
56142             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56143             if(dds){
56144                 for(var dd in dds){
56145                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56146                         var elid = dds[dd].dragElId;
56147                         dds[dd].unreg();
56148                         Roo.get(elid).remove();
56149                     } else if(dds[dd].config.isTarget){
56150                         dds[dd].proxyTop.remove();
56151                         dds[dd].proxyBottom.remove();
56152                         dds[dd].unreg();
56153                     }
56154                     if(Roo.dd.DDM.locationCache[dd]){
56155                         delete Roo.dd.DDM.locationCache[dd];
56156                     }
56157                 }
56158                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56159             }
56160         }
56161         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56162         this.bind(null, null);
56163         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56164     },
56165
56166     handleLockChange : function(){
56167         this.refresh(true);
56168     },
56169
56170     onDenyColumnLock : function(){
56171
56172     },
56173
56174     onDenyColumnHide : function(){
56175
56176     },
56177
56178     handleHdMenuClick : function(item){
56179         var index = this.hdCtxIndex;
56180         var cm = this.cm, ds = this.ds;
56181         switch(item.id){
56182             case "asc":
56183                 ds.sort(cm.getDataIndex(index), "ASC");
56184                 break;
56185             case "desc":
56186                 ds.sort(cm.getDataIndex(index), "DESC");
56187                 break;
56188             case "lock":
56189                 var lc = cm.getLockedCount();
56190                 if(cm.getColumnCount(true) <= lc+1){
56191                     this.onDenyColumnLock();
56192                     return;
56193                 }
56194                 if(lc != index){
56195                     cm.setLocked(index, true, true);
56196                     cm.moveColumn(index, lc);
56197                     this.grid.fireEvent("columnmove", index, lc);
56198                 }else{
56199                     cm.setLocked(index, true);
56200                 }
56201             break;
56202             case "unlock":
56203                 var lc = cm.getLockedCount();
56204                 if((lc-1) != index){
56205                     cm.setLocked(index, false, true);
56206                     cm.moveColumn(index, lc-1);
56207                     this.grid.fireEvent("columnmove", index, lc-1);
56208                 }else{
56209                     cm.setLocked(index, false);
56210                 }
56211             break;
56212             case 'wider': // used to expand cols on touch..
56213             case 'narrow':
56214                 var cw = cm.getColumnWidth(index);
56215                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56216                 cw = Math.max(0, cw);
56217                 cw = Math.min(cw,4000);
56218                 cm.setColumnWidth(index, cw);
56219                 break;
56220                 
56221             default:
56222                 index = cm.getIndexById(item.id.substr(4));
56223                 if(index != -1){
56224                     if(item.checked && cm.getColumnCount(true) <= 1){
56225                         this.onDenyColumnHide();
56226                         return false;
56227                     }
56228                     cm.setHidden(index, item.checked);
56229                 }
56230         }
56231         return true;
56232     },
56233
56234     beforeColMenuShow : function(){
56235         var cm = this.cm,  colCount = cm.getColumnCount();
56236         this.colMenu.removeAll();
56237         for(var i = 0; i < colCount; i++){
56238             this.colMenu.add(new Roo.menu.CheckItem({
56239                 id: "col-"+cm.getColumnId(i),
56240                 text: cm.getColumnHeader(i),
56241                 checked: !cm.isHidden(i),
56242                 hideOnClick:false
56243             }));
56244         }
56245     },
56246
56247     handleHdCtx : function(g, index, e){
56248         e.stopEvent();
56249         var hd = this.getHeaderCell(index);
56250         this.hdCtxIndex = index;
56251         var ms = this.hmenu.items, cm = this.cm;
56252         ms.get("asc").setDisabled(!cm.isSortable(index));
56253         ms.get("desc").setDisabled(!cm.isSortable(index));
56254         if(this.grid.enableColLock !== false){
56255             ms.get("lock").setDisabled(cm.isLocked(index));
56256             ms.get("unlock").setDisabled(!cm.isLocked(index));
56257         }
56258         this.hmenu.show(hd, "tl-bl");
56259     },
56260
56261     handleHdOver : function(e){
56262         var hd = this.findHeaderCell(e.getTarget());
56263         if(hd && !this.headersDisabled){
56264             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56265                this.fly(hd).addClass("x-grid-hd-over");
56266             }
56267         }
56268     },
56269
56270     handleHdOut : function(e){
56271         var hd = this.findHeaderCell(e.getTarget());
56272         if(hd){
56273             this.fly(hd).removeClass("x-grid-hd-over");
56274         }
56275     },
56276
56277     handleSplitDblClick : function(e, t){
56278         var i = this.getCellIndex(t);
56279         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56280             this.autoSizeColumn(i, true);
56281             this.layout();
56282         }
56283     },
56284
56285     render : function(){
56286
56287         var cm = this.cm;
56288         var colCount = cm.getColumnCount();
56289
56290         if(this.grid.monitorWindowResize === true){
56291             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56292         }
56293         var header = this.renderHeaders();
56294         var body = this.templates.body.apply({rows:""});
56295         var html = this.templates.master.apply({
56296             lockedBody: body,
56297             body: body,
56298             lockedHeader: header[0],
56299             header: header[1]
56300         });
56301
56302         //this.updateColumns();
56303
56304         this.grid.getGridEl().dom.innerHTML = html;
56305
56306         this.initElements();
56307         
56308         // a kludge to fix the random scolling effect in webkit
56309         this.el.on("scroll", function() {
56310             this.el.dom.scrollTop=0; // hopefully not recursive..
56311         },this);
56312
56313         this.scroller.on("scroll", this.handleScroll, this);
56314         this.lockedBody.on("mousewheel", this.handleWheel, this);
56315         this.mainBody.on("mousewheel", this.handleWheel, this);
56316
56317         this.mainHd.on("mouseover", this.handleHdOver, this);
56318         this.mainHd.on("mouseout", this.handleHdOut, this);
56319         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56320                 {delegate: "."+this.splitClass});
56321
56322         this.lockedHd.on("mouseover", this.handleHdOver, this);
56323         this.lockedHd.on("mouseout", this.handleHdOut, this);
56324         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56325                 {delegate: "."+this.splitClass});
56326
56327         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56328             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56329         }
56330
56331         this.updateSplitters();
56332
56333         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56334             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56335             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56336         }
56337
56338         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56339             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56340             this.hmenu.add(
56341                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56342                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56343             );
56344             if(this.grid.enableColLock !== false){
56345                 this.hmenu.add('-',
56346                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56347                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56348                 );
56349             }
56350             if (Roo.isTouch) {
56351                  this.hmenu.add('-',
56352                     {id:"wider", text: this.columnsWiderText},
56353                     {id:"narrow", text: this.columnsNarrowText }
56354                 );
56355                 
56356                  
56357             }
56358             
56359             if(this.grid.enableColumnHide !== false){
56360
56361                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56362                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56363                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56364
56365                 this.hmenu.add('-',
56366                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56367                 );
56368             }
56369             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56370
56371             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56372         }
56373
56374         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56375             this.dd = new Roo.grid.GridDragZone(this.grid, {
56376                 ddGroup : this.grid.ddGroup || 'GridDD'
56377             });
56378             
56379         }
56380
56381         /*
56382         for(var i = 0; i < colCount; i++){
56383             if(cm.isHidden(i)){
56384                 this.hideColumn(i);
56385             }
56386             if(cm.config[i].align){
56387                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56388                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56389             }
56390         }*/
56391         
56392         this.updateHeaderSortState();
56393
56394         this.beforeInitialResize();
56395         this.layout(true);
56396
56397         // two part rendering gives faster view to the user
56398         this.renderPhase2.defer(1, this);
56399     },
56400
56401     renderPhase2 : function(){
56402         // render the rows now
56403         this.refresh();
56404         if(this.grid.autoSizeColumns){
56405             this.autoSizeColumns();
56406         }
56407     },
56408
56409     beforeInitialResize : function(){
56410
56411     },
56412
56413     onColumnSplitterMoved : function(i, w){
56414         this.userResized = true;
56415         var cm = this.grid.colModel;
56416         cm.setColumnWidth(i, w, true);
56417         var cid = cm.getColumnId(i);
56418         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56419         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56420         this.updateSplitters();
56421         this.layout();
56422         this.grid.fireEvent("columnresize", i, w);
56423     },
56424
56425     syncRowHeights : function(startIndex, endIndex){
56426         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56427             startIndex = startIndex || 0;
56428             var mrows = this.getBodyTable().rows;
56429             var lrows = this.getLockedTable().rows;
56430             var len = mrows.length-1;
56431             endIndex = Math.min(endIndex || len, len);
56432             for(var i = startIndex; i <= endIndex; i++){
56433                 var m = mrows[i], l = lrows[i];
56434                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56435                 m.style.height = l.style.height = h + "px";
56436             }
56437         }
56438     },
56439
56440     layout : function(initialRender, is2ndPass){
56441         var g = this.grid;
56442         var auto = g.autoHeight;
56443         var scrollOffset = 16;
56444         var c = g.getGridEl(), cm = this.cm,
56445                 expandCol = g.autoExpandColumn,
56446                 gv = this;
56447         //c.beginMeasure();
56448
56449         if(!c.dom.offsetWidth){ // display:none?
56450             if(initialRender){
56451                 this.lockedWrap.show();
56452                 this.mainWrap.show();
56453             }
56454             return;
56455         }
56456
56457         var hasLock = this.cm.isLocked(0);
56458
56459         var tbh = this.headerPanel.getHeight();
56460         var bbh = this.footerPanel.getHeight();
56461
56462         if(auto){
56463             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56464             var newHeight = ch + c.getBorderWidth("tb");
56465             if(g.maxHeight){
56466                 newHeight = Math.min(g.maxHeight, newHeight);
56467             }
56468             c.setHeight(newHeight);
56469         }
56470
56471         if(g.autoWidth){
56472             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56473         }
56474
56475         var s = this.scroller;
56476
56477         var csize = c.getSize(true);
56478
56479         this.el.setSize(csize.width, csize.height);
56480
56481         this.headerPanel.setWidth(csize.width);
56482         this.footerPanel.setWidth(csize.width);
56483
56484         var hdHeight = this.mainHd.getHeight();
56485         var vw = csize.width;
56486         var vh = csize.height - (tbh + bbh);
56487
56488         s.setSize(vw, vh);
56489
56490         var bt = this.getBodyTable();
56491         
56492         if(cm.getLockedCount() == cm.config.length){
56493             bt = this.getLockedTable();
56494         }
56495         
56496         var ltWidth = hasLock ?
56497                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56498
56499         var scrollHeight = bt.offsetHeight;
56500         var scrollWidth = ltWidth + bt.offsetWidth;
56501         var vscroll = false, hscroll = false;
56502
56503         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56504
56505         var lw = this.lockedWrap, mw = this.mainWrap;
56506         var lb = this.lockedBody, mb = this.mainBody;
56507
56508         setTimeout(function(){
56509             var t = s.dom.offsetTop;
56510             var w = s.dom.clientWidth,
56511                 h = s.dom.clientHeight;
56512
56513             lw.setTop(t);
56514             lw.setSize(ltWidth, h);
56515
56516             mw.setLeftTop(ltWidth, t);
56517             mw.setSize(w-ltWidth, h);
56518
56519             lb.setHeight(h-hdHeight);
56520             mb.setHeight(h-hdHeight);
56521
56522             if(is2ndPass !== true && !gv.userResized && expandCol){
56523                 // high speed resize without full column calculation
56524                 
56525                 var ci = cm.getIndexById(expandCol);
56526                 if (ci < 0) {
56527                     ci = cm.findColumnIndex(expandCol);
56528                 }
56529                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56530                 var expandId = cm.getColumnId(ci);
56531                 var  tw = cm.getTotalWidth(false);
56532                 var currentWidth = cm.getColumnWidth(ci);
56533                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56534                 if(currentWidth != cw){
56535                     cm.setColumnWidth(ci, cw, true);
56536                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56537                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56538                     gv.updateSplitters();
56539                     gv.layout(false, true);
56540                 }
56541             }
56542
56543             if(initialRender){
56544                 lw.show();
56545                 mw.show();
56546             }
56547             //c.endMeasure();
56548         }, 10);
56549     },
56550
56551     onWindowResize : function(){
56552         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56553             return;
56554         }
56555         this.layout();
56556     },
56557
56558     appendFooter : function(parentEl){
56559         return null;
56560     },
56561
56562     sortAscText : "Sort Ascending",
56563     sortDescText : "Sort Descending",
56564     lockText : "Lock Column",
56565     unlockText : "Unlock Column",
56566     columnsText : "Columns",
56567  
56568     columnsWiderText : "Wider",
56569     columnsNarrowText : "Thinner"
56570 });
56571
56572
56573 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56574     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56575     this.proxy.el.addClass('x-grid3-col-dd');
56576 };
56577
56578 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56579     handleMouseDown : function(e){
56580
56581     },
56582
56583     callHandleMouseDown : function(e){
56584         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56585     }
56586 });
56587 /*
56588  * Based on:
56589  * Ext JS Library 1.1.1
56590  * Copyright(c) 2006-2007, Ext JS, LLC.
56591  *
56592  * Originally Released Under LGPL - original licence link has changed is not relivant.
56593  *
56594  * Fork - LGPL
56595  * <script type="text/javascript">
56596  */
56597  
56598 // private
56599 // This is a support class used internally by the Grid components
56600 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56601     this.grid = grid;
56602     this.view = grid.getView();
56603     this.proxy = this.view.resizeProxy;
56604     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56605         "gridSplitters" + this.grid.getGridEl().id, {
56606         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56607     });
56608     this.setHandleElId(Roo.id(hd));
56609     this.setOuterHandleElId(Roo.id(hd2));
56610     this.scroll = false;
56611 };
56612 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56613     fly: Roo.Element.fly,
56614
56615     b4StartDrag : function(x, y){
56616         this.view.headersDisabled = true;
56617         this.proxy.setHeight(this.view.mainWrap.getHeight());
56618         var w = this.cm.getColumnWidth(this.cellIndex);
56619         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56620         this.resetConstraints();
56621         this.setXConstraint(minw, 1000);
56622         this.setYConstraint(0, 0);
56623         this.minX = x - minw;
56624         this.maxX = x + 1000;
56625         this.startPos = x;
56626         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56627     },
56628
56629
56630     handleMouseDown : function(e){
56631         ev = Roo.EventObject.setEvent(e);
56632         var t = this.fly(ev.getTarget());
56633         if(t.hasClass("x-grid-split")){
56634             this.cellIndex = this.view.getCellIndex(t.dom);
56635             this.split = t.dom;
56636             this.cm = this.grid.colModel;
56637             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56638                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56639             }
56640         }
56641     },
56642
56643     endDrag : function(e){
56644         this.view.headersDisabled = false;
56645         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56646         var diff = endX - this.startPos;
56647         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56648     },
56649
56650     autoOffset : function(){
56651         this.setDelta(0,0);
56652     }
56653 });/*
56654  * Based on:
56655  * Ext JS Library 1.1.1
56656  * Copyright(c) 2006-2007, Ext JS, LLC.
56657  *
56658  * Originally Released Under LGPL - original licence link has changed is not relivant.
56659  *
56660  * Fork - LGPL
56661  * <script type="text/javascript">
56662  */
56663  
56664 // private
56665 // This is a support class used internally by the Grid components
56666 Roo.grid.GridDragZone = function(grid, config){
56667     this.view = grid.getView();
56668     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56669     if(this.view.lockedBody){
56670         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56671         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56672     }
56673     this.scroll = false;
56674     this.grid = grid;
56675     this.ddel = document.createElement('div');
56676     this.ddel.className = 'x-grid-dd-wrap';
56677 };
56678
56679 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56680     ddGroup : "GridDD",
56681
56682     getDragData : function(e){
56683         var t = Roo.lib.Event.getTarget(e);
56684         var rowIndex = this.view.findRowIndex(t);
56685         var sm = this.grid.selModel;
56686             
56687         //Roo.log(rowIndex);
56688         
56689         if (sm.getSelectedCell) {
56690             // cell selection..
56691             if (!sm.getSelectedCell()) {
56692                 return false;
56693             }
56694             if (rowIndex != sm.getSelectedCell()[0]) {
56695                 return false;
56696             }
56697         
56698         }
56699         
56700         if(rowIndex !== false){
56701             
56702             // if editorgrid.. 
56703             
56704             
56705             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56706                
56707             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56708               //  
56709             //}
56710             if (e.hasModifier()){
56711                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56712             }
56713             
56714             Roo.log("getDragData");
56715             
56716             return {
56717                 grid: this.grid,
56718                 ddel: this.ddel,
56719                 rowIndex: rowIndex,
56720                 selections:sm.getSelections ? sm.getSelections() : (
56721                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56722                 )
56723             };
56724         }
56725         return false;
56726     },
56727
56728     onInitDrag : function(e){
56729         var data = this.dragData;
56730         this.ddel.innerHTML = this.grid.getDragDropText();
56731         this.proxy.update(this.ddel);
56732         // fire start drag?
56733     },
56734
56735     afterRepair : function(){
56736         this.dragging = false;
56737     },
56738
56739     getRepairXY : function(e, data){
56740         return false;
56741     },
56742
56743     onEndDrag : function(data, e){
56744         // fire end drag?
56745     },
56746
56747     onValidDrop : function(dd, e, id){
56748         // fire drag drop?
56749         this.hideProxy();
56750     },
56751
56752     beforeInvalidDrop : function(e, id){
56753
56754     }
56755 });/*
56756  * Based on:
56757  * Ext JS Library 1.1.1
56758  * Copyright(c) 2006-2007, Ext JS, LLC.
56759  *
56760  * Originally Released Under LGPL - original licence link has changed is not relivant.
56761  *
56762  * Fork - LGPL
56763  * <script type="text/javascript">
56764  */
56765  
56766
56767 /**
56768  * @class Roo.grid.ColumnModel
56769  * @extends Roo.util.Observable
56770  * This is the default implementation of a ColumnModel used by the Grid. It defines
56771  * the columns in the grid.
56772  * <br>Usage:<br>
56773  <pre><code>
56774  var colModel = new Roo.grid.ColumnModel([
56775         {header: "Ticker", width: 60, sortable: true, locked: true},
56776         {header: "Company Name", width: 150, sortable: true},
56777         {header: "Market Cap.", width: 100, sortable: true},
56778         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56779         {header: "Employees", width: 100, sortable: true, resizable: false}
56780  ]);
56781  </code></pre>
56782  * <p>
56783  
56784  * The config options listed for this class are options which may appear in each
56785  * individual column definition.
56786  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56787  * @constructor
56788  * @param {Object} config An Array of column config objects. See this class's
56789  * config objects for details.
56790 */
56791 Roo.grid.ColumnModel = function(config){
56792         /**
56793      * The config passed into the constructor
56794      */
56795     this.config = config;
56796     this.lookup = {};
56797
56798     // if no id, create one
56799     // if the column does not have a dataIndex mapping,
56800     // map it to the order it is in the config
56801     for(var i = 0, len = config.length; i < len; i++){
56802         var c = config[i];
56803         if(typeof c.dataIndex == "undefined"){
56804             c.dataIndex = i;
56805         }
56806         if(typeof c.renderer == "string"){
56807             c.renderer = Roo.util.Format[c.renderer];
56808         }
56809         if(typeof c.id == "undefined"){
56810             c.id = Roo.id();
56811         }
56812         if(c.editor && c.editor.xtype){
56813             c.editor  = Roo.factory(c.editor, Roo.grid);
56814         }
56815         if(c.editor && c.editor.isFormField){
56816             c.editor = new Roo.grid.GridEditor(c.editor);
56817         }
56818         this.lookup[c.id] = c;
56819     }
56820
56821     /**
56822      * The width of columns which have no width specified (defaults to 100)
56823      * @type Number
56824      */
56825     this.defaultWidth = 100;
56826
56827     /**
56828      * Default sortable of columns which have no sortable specified (defaults to false)
56829      * @type Boolean
56830      */
56831     this.defaultSortable = false;
56832
56833     this.addEvents({
56834         /**
56835              * @event widthchange
56836              * Fires when the width of a column changes.
56837              * @param {ColumnModel} this
56838              * @param {Number} columnIndex The column index
56839              * @param {Number} newWidth The new width
56840              */
56841             "widthchange": true,
56842         /**
56843              * @event headerchange
56844              * Fires when the text of a header changes.
56845              * @param {ColumnModel} this
56846              * @param {Number} columnIndex The column index
56847              * @param {Number} newText The new header text
56848              */
56849             "headerchange": true,
56850         /**
56851              * @event hiddenchange
56852              * Fires when a column is hidden or "unhidden".
56853              * @param {ColumnModel} this
56854              * @param {Number} columnIndex The column index
56855              * @param {Boolean} hidden true if hidden, false otherwise
56856              */
56857             "hiddenchange": true,
56858             /**
56859          * @event columnmoved
56860          * Fires when a column is moved.
56861          * @param {ColumnModel} this
56862          * @param {Number} oldIndex
56863          * @param {Number} newIndex
56864          */
56865         "columnmoved" : true,
56866         /**
56867          * @event columlockchange
56868          * Fires when a column's locked state is changed
56869          * @param {ColumnModel} this
56870          * @param {Number} colIndex
56871          * @param {Boolean} locked true if locked
56872          */
56873         "columnlockchange" : true
56874     });
56875     Roo.grid.ColumnModel.superclass.constructor.call(this);
56876 };
56877 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
56878     /**
56879      * @cfg {String} header The header text to display in the Grid view.
56880      */
56881     /**
56882      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
56883      * {@link Roo.data.Record} definition from which to draw the column's value. If not
56884      * specified, the column's index is used as an index into the Record's data Array.
56885      */
56886     /**
56887      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
56888      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
56889      */
56890     /**
56891      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
56892      * Defaults to the value of the {@link #defaultSortable} property.
56893      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
56894      */
56895     /**
56896      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
56897      */
56898     /**
56899      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
56900      */
56901     /**
56902      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
56903      */
56904     /**
56905      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
56906      */
56907     /**
56908      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
56909      * given the cell's data value. See {@link #setRenderer}. If not specified, the
56910      * default renderer uses the raw data value. If an object is returned (bootstrap only)
56911      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
56912      */
56913        /**
56914      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
56915      */
56916     /**
56917      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
56918      */
56919     /**
56920      * @cfg {String} cursor (Optional)
56921      */
56922     /**
56923      * @cfg {String} tooltip (Optional)
56924      */
56925     /**
56926      * @cfg {Number} xs (Optional)
56927      */
56928     /**
56929      * @cfg {Number} sm (Optional)
56930      */
56931     /**
56932      * @cfg {Number} md (Optional)
56933      */
56934     /**
56935      * @cfg {Number} lg (Optional)
56936      */
56937     /**
56938      * Returns the id of the column at the specified index.
56939      * @param {Number} index The column index
56940      * @return {String} the id
56941      */
56942     getColumnId : function(index){
56943         return this.config[index].id;
56944     },
56945
56946     /**
56947      * Returns the column for a specified id.
56948      * @param {String} id The column id
56949      * @return {Object} the column
56950      */
56951     getColumnById : function(id){
56952         return this.lookup[id];
56953     },
56954
56955     
56956     /**
56957      * Returns the column for a specified dataIndex.
56958      * @param {String} dataIndex The column dataIndex
56959      * @return {Object|Boolean} the column or false if not found
56960      */
56961     getColumnByDataIndex: function(dataIndex){
56962         var index = this.findColumnIndex(dataIndex);
56963         return index > -1 ? this.config[index] : false;
56964     },
56965     
56966     /**
56967      * Returns the index for a specified column id.
56968      * @param {String} id The column id
56969      * @return {Number} the index, or -1 if not found
56970      */
56971     getIndexById : function(id){
56972         for(var i = 0, len = this.config.length; i < len; i++){
56973             if(this.config[i].id == id){
56974                 return i;
56975             }
56976         }
56977         return -1;
56978     },
56979     
56980     /**
56981      * Returns the index for a specified column dataIndex.
56982      * @param {String} dataIndex The column dataIndex
56983      * @return {Number} the index, or -1 if not found
56984      */
56985     
56986     findColumnIndex : function(dataIndex){
56987         for(var i = 0, len = this.config.length; i < len; i++){
56988             if(this.config[i].dataIndex == dataIndex){
56989                 return i;
56990             }
56991         }
56992         return -1;
56993     },
56994     
56995     
56996     moveColumn : function(oldIndex, newIndex){
56997         var c = this.config[oldIndex];
56998         this.config.splice(oldIndex, 1);
56999         this.config.splice(newIndex, 0, c);
57000         this.dataMap = null;
57001         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57002     },
57003
57004     isLocked : function(colIndex){
57005         return this.config[colIndex].locked === true;
57006     },
57007
57008     setLocked : function(colIndex, value, suppressEvent){
57009         if(this.isLocked(colIndex) == value){
57010             return;
57011         }
57012         this.config[colIndex].locked = value;
57013         if(!suppressEvent){
57014             this.fireEvent("columnlockchange", this, colIndex, value);
57015         }
57016     },
57017
57018     getTotalLockedWidth : function(){
57019         var totalWidth = 0;
57020         for(var i = 0; i < this.config.length; i++){
57021             if(this.isLocked(i) && !this.isHidden(i)){
57022                 this.totalWidth += this.getColumnWidth(i);
57023             }
57024         }
57025         return totalWidth;
57026     },
57027
57028     getLockedCount : function(){
57029         for(var i = 0, len = this.config.length; i < len; i++){
57030             if(!this.isLocked(i)){
57031                 return i;
57032             }
57033         }
57034         
57035         return this.config.length;
57036     },
57037
57038     /**
57039      * Returns the number of columns.
57040      * @return {Number}
57041      */
57042     getColumnCount : function(visibleOnly){
57043         if(visibleOnly === true){
57044             var c = 0;
57045             for(var i = 0, len = this.config.length; i < len; i++){
57046                 if(!this.isHidden(i)){
57047                     c++;
57048                 }
57049             }
57050             return c;
57051         }
57052         return this.config.length;
57053     },
57054
57055     /**
57056      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57057      * @param {Function} fn
57058      * @param {Object} scope (optional)
57059      * @return {Array} result
57060      */
57061     getColumnsBy : function(fn, scope){
57062         var r = [];
57063         for(var i = 0, len = this.config.length; i < len; i++){
57064             var c = this.config[i];
57065             if(fn.call(scope||this, c, i) === true){
57066                 r[r.length] = c;
57067             }
57068         }
57069         return r;
57070     },
57071
57072     /**
57073      * Returns true if the specified column is sortable.
57074      * @param {Number} col The column index
57075      * @return {Boolean}
57076      */
57077     isSortable : function(col){
57078         if(typeof this.config[col].sortable == "undefined"){
57079             return this.defaultSortable;
57080         }
57081         return this.config[col].sortable;
57082     },
57083
57084     /**
57085      * Returns the rendering (formatting) function defined for the column.
57086      * @param {Number} col The column index.
57087      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57088      */
57089     getRenderer : function(col){
57090         if(!this.config[col].renderer){
57091             return Roo.grid.ColumnModel.defaultRenderer;
57092         }
57093         return this.config[col].renderer;
57094     },
57095
57096     /**
57097      * Sets the rendering (formatting) function for a column.
57098      * @param {Number} col The column index
57099      * @param {Function} fn The function to use to process the cell's raw data
57100      * to return HTML markup for the grid view. The render function is called with
57101      * the following parameters:<ul>
57102      * <li>Data value.</li>
57103      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57104      * <li>css A CSS style string to apply to the table cell.</li>
57105      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57106      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57107      * <li>Row index</li>
57108      * <li>Column index</li>
57109      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57110      */
57111     setRenderer : function(col, fn){
57112         this.config[col].renderer = fn;
57113     },
57114
57115     /**
57116      * Returns the width for the specified column.
57117      * @param {Number} col The column index
57118      * @return {Number}
57119      */
57120     getColumnWidth : function(col){
57121         return this.config[col].width * 1 || this.defaultWidth;
57122     },
57123
57124     /**
57125      * Sets the width for a column.
57126      * @param {Number} col The column index
57127      * @param {Number} width The new width
57128      */
57129     setColumnWidth : function(col, width, suppressEvent){
57130         this.config[col].width = width;
57131         this.totalWidth = null;
57132         if(!suppressEvent){
57133              this.fireEvent("widthchange", this, col, width);
57134         }
57135     },
57136
57137     /**
57138      * Returns the total width of all columns.
57139      * @param {Boolean} includeHidden True to include hidden column widths
57140      * @return {Number}
57141      */
57142     getTotalWidth : function(includeHidden){
57143         if(!this.totalWidth){
57144             this.totalWidth = 0;
57145             for(var i = 0, len = this.config.length; i < len; i++){
57146                 if(includeHidden || !this.isHidden(i)){
57147                     this.totalWidth += this.getColumnWidth(i);
57148                 }
57149             }
57150         }
57151         return this.totalWidth;
57152     },
57153
57154     /**
57155      * Returns the header for the specified column.
57156      * @param {Number} col The column index
57157      * @return {String}
57158      */
57159     getColumnHeader : function(col){
57160         return this.config[col].header;
57161     },
57162
57163     /**
57164      * Sets the header for a column.
57165      * @param {Number} col The column index
57166      * @param {String} header The new header
57167      */
57168     setColumnHeader : function(col, header){
57169         this.config[col].header = header;
57170         this.fireEvent("headerchange", this, col, header);
57171     },
57172
57173     /**
57174      * Returns the tooltip for the specified column.
57175      * @param {Number} col The column index
57176      * @return {String}
57177      */
57178     getColumnTooltip : function(col){
57179             return this.config[col].tooltip;
57180     },
57181     /**
57182      * Sets the tooltip for a column.
57183      * @param {Number} col The column index
57184      * @param {String} tooltip The new tooltip
57185      */
57186     setColumnTooltip : function(col, tooltip){
57187             this.config[col].tooltip = tooltip;
57188     },
57189
57190     /**
57191      * Returns the dataIndex for the specified column.
57192      * @param {Number} col The column index
57193      * @return {Number}
57194      */
57195     getDataIndex : function(col){
57196         return this.config[col].dataIndex;
57197     },
57198
57199     /**
57200      * Sets the dataIndex for a column.
57201      * @param {Number} col The column index
57202      * @param {Number} dataIndex The new dataIndex
57203      */
57204     setDataIndex : function(col, dataIndex){
57205         this.config[col].dataIndex = dataIndex;
57206     },
57207
57208     
57209     
57210     /**
57211      * Returns true if the cell is editable.
57212      * @param {Number} colIndex The column index
57213      * @param {Number} rowIndex The row index - this is nto actually used..?
57214      * @return {Boolean}
57215      */
57216     isCellEditable : function(colIndex, rowIndex){
57217         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57218     },
57219
57220     /**
57221      * Returns the editor defined for the cell/column.
57222      * return false or null to disable editing.
57223      * @param {Number} colIndex The column index
57224      * @param {Number} rowIndex The row index
57225      * @return {Object}
57226      */
57227     getCellEditor : function(colIndex, rowIndex){
57228         return this.config[colIndex].editor;
57229     },
57230
57231     /**
57232      * Sets if a column is editable.
57233      * @param {Number} col The column index
57234      * @param {Boolean} editable True if the column is editable
57235      */
57236     setEditable : function(col, editable){
57237         this.config[col].editable = editable;
57238     },
57239
57240
57241     /**
57242      * Returns true if the column is hidden.
57243      * @param {Number} colIndex The column index
57244      * @return {Boolean}
57245      */
57246     isHidden : function(colIndex){
57247         return this.config[colIndex].hidden;
57248     },
57249
57250
57251     /**
57252      * Returns true if the column width cannot be changed
57253      */
57254     isFixed : function(colIndex){
57255         return this.config[colIndex].fixed;
57256     },
57257
57258     /**
57259      * Returns true if the column can be resized
57260      * @return {Boolean}
57261      */
57262     isResizable : function(colIndex){
57263         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57264     },
57265     /**
57266      * Sets if a column is hidden.
57267      * @param {Number} colIndex The column index
57268      * @param {Boolean} hidden True if the column is hidden
57269      */
57270     setHidden : function(colIndex, hidden){
57271         this.config[colIndex].hidden = hidden;
57272         this.totalWidth = null;
57273         this.fireEvent("hiddenchange", this, colIndex, hidden);
57274     },
57275
57276     /**
57277      * Sets the editor for a column.
57278      * @param {Number} col The column index
57279      * @param {Object} editor The editor object
57280      */
57281     setEditor : function(col, editor){
57282         this.config[col].editor = editor;
57283     }
57284 });
57285
57286 Roo.grid.ColumnModel.defaultRenderer = function(value){
57287         if(typeof value == "string" && value.length < 1){
57288             return "&#160;";
57289         }
57290         return value;
57291 };
57292
57293 // Alias for backwards compatibility
57294 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57295 /*
57296  * Based on:
57297  * Ext JS Library 1.1.1
57298  * Copyright(c) 2006-2007, Ext JS, LLC.
57299  *
57300  * Originally Released Under LGPL - original licence link has changed is not relivant.
57301  *
57302  * Fork - LGPL
57303  * <script type="text/javascript">
57304  */
57305
57306 /**
57307  * @class Roo.grid.AbstractSelectionModel
57308  * @extends Roo.util.Observable
57309  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57310  * implemented by descendant classes.  This class should not be directly instantiated.
57311  * @constructor
57312  */
57313 Roo.grid.AbstractSelectionModel = function(){
57314     this.locked = false;
57315     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57316 };
57317
57318 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57319     /** @ignore Called by the grid automatically. Do not call directly. */
57320     init : function(grid){
57321         this.grid = grid;
57322         this.initEvents();
57323     },
57324
57325     /**
57326      * Locks the selections.
57327      */
57328     lock : function(){
57329         this.locked = true;
57330     },
57331
57332     /**
57333      * Unlocks the selections.
57334      */
57335     unlock : function(){
57336         this.locked = false;
57337     },
57338
57339     /**
57340      * Returns true if the selections are locked.
57341      * @return {Boolean}
57342      */
57343     isLocked : function(){
57344         return this.locked;
57345     }
57346 });/*
57347  * Based on:
57348  * Ext JS Library 1.1.1
57349  * Copyright(c) 2006-2007, Ext JS, LLC.
57350  *
57351  * Originally Released Under LGPL - original licence link has changed is not relivant.
57352  *
57353  * Fork - LGPL
57354  * <script type="text/javascript">
57355  */
57356 /**
57357  * @extends Roo.grid.AbstractSelectionModel
57358  * @class Roo.grid.RowSelectionModel
57359  * The default SelectionModel used by {@link Roo.grid.Grid}.
57360  * It supports multiple selections and keyboard selection/navigation. 
57361  * @constructor
57362  * @param {Object} config
57363  */
57364 Roo.grid.RowSelectionModel = function(config){
57365     Roo.apply(this, config);
57366     this.selections = new Roo.util.MixedCollection(false, function(o){
57367         return o.id;
57368     });
57369
57370     this.last = false;
57371     this.lastActive = false;
57372
57373     this.addEvents({
57374         /**
57375              * @event selectionchange
57376              * Fires when the selection changes
57377              * @param {SelectionModel} this
57378              */
57379             "selectionchange" : true,
57380         /**
57381              * @event afterselectionchange
57382              * Fires after the selection changes (eg. by key press or clicking)
57383              * @param {SelectionModel} this
57384              */
57385             "afterselectionchange" : true,
57386         /**
57387              * @event beforerowselect
57388              * Fires when a row is selected being selected, return false to cancel.
57389              * @param {SelectionModel} this
57390              * @param {Number} rowIndex The selected index
57391              * @param {Boolean} keepExisting False if other selections will be cleared
57392              */
57393             "beforerowselect" : true,
57394         /**
57395              * @event rowselect
57396              * Fires when a row is selected.
57397              * @param {SelectionModel} this
57398              * @param {Number} rowIndex The selected index
57399              * @param {Roo.data.Record} r The record
57400              */
57401             "rowselect" : true,
57402         /**
57403              * @event rowdeselect
57404              * Fires when a row is deselected.
57405              * @param {SelectionModel} this
57406              * @param {Number} rowIndex The selected index
57407              */
57408         "rowdeselect" : true
57409     });
57410     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57411     this.locked = false;
57412 };
57413
57414 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57415     /**
57416      * @cfg {Boolean} singleSelect
57417      * True to allow selection of only one row at a time (defaults to false)
57418      */
57419     singleSelect : false,
57420
57421     // private
57422     initEvents : function(){
57423
57424         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57425             this.grid.on("mousedown", this.handleMouseDown, this);
57426         }else{ // allow click to work like normal
57427             this.grid.on("rowclick", this.handleDragableRowClick, this);
57428         }
57429
57430         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57431             "up" : function(e){
57432                 if(!e.shiftKey){
57433                     this.selectPrevious(e.shiftKey);
57434                 }else if(this.last !== false && this.lastActive !== false){
57435                     var last = this.last;
57436                     this.selectRange(this.last,  this.lastActive-1);
57437                     this.grid.getView().focusRow(this.lastActive);
57438                     if(last !== false){
57439                         this.last = last;
57440                     }
57441                 }else{
57442                     this.selectFirstRow();
57443                 }
57444                 this.fireEvent("afterselectionchange", this);
57445             },
57446             "down" : function(e){
57447                 if(!e.shiftKey){
57448                     this.selectNext(e.shiftKey);
57449                 }else if(this.last !== false && this.lastActive !== false){
57450                     var last = this.last;
57451                     this.selectRange(this.last,  this.lastActive+1);
57452                     this.grid.getView().focusRow(this.lastActive);
57453                     if(last !== false){
57454                         this.last = last;
57455                     }
57456                 }else{
57457                     this.selectFirstRow();
57458                 }
57459                 this.fireEvent("afterselectionchange", this);
57460             },
57461             scope: this
57462         });
57463
57464         var view = this.grid.view;
57465         view.on("refresh", this.onRefresh, this);
57466         view.on("rowupdated", this.onRowUpdated, this);
57467         view.on("rowremoved", this.onRemove, this);
57468     },
57469
57470     // private
57471     onRefresh : function(){
57472         var ds = this.grid.dataSource, i, v = this.grid.view;
57473         var s = this.selections;
57474         s.each(function(r){
57475             if((i = ds.indexOfId(r.id)) != -1){
57476                 v.onRowSelect(i);
57477                 s.add(ds.getAt(i)); // updating the selection relate data
57478             }else{
57479                 s.remove(r);
57480             }
57481         });
57482     },
57483
57484     // private
57485     onRemove : function(v, index, r){
57486         this.selections.remove(r);
57487     },
57488
57489     // private
57490     onRowUpdated : function(v, index, r){
57491         if(this.isSelected(r)){
57492             v.onRowSelect(index);
57493         }
57494     },
57495
57496     /**
57497      * Select records.
57498      * @param {Array} records The records to select
57499      * @param {Boolean} keepExisting (optional) True to keep existing selections
57500      */
57501     selectRecords : function(records, keepExisting){
57502         if(!keepExisting){
57503             this.clearSelections();
57504         }
57505         var ds = this.grid.dataSource;
57506         for(var i = 0, len = records.length; i < len; i++){
57507             this.selectRow(ds.indexOf(records[i]), true);
57508         }
57509     },
57510
57511     /**
57512      * Gets the number of selected rows.
57513      * @return {Number}
57514      */
57515     getCount : function(){
57516         return this.selections.length;
57517     },
57518
57519     /**
57520      * Selects the first row in the grid.
57521      */
57522     selectFirstRow : function(){
57523         this.selectRow(0);
57524     },
57525
57526     /**
57527      * Select the last row.
57528      * @param {Boolean} keepExisting (optional) True to keep existing selections
57529      */
57530     selectLastRow : function(keepExisting){
57531         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57532     },
57533
57534     /**
57535      * Selects the row immediately following the last selected row.
57536      * @param {Boolean} keepExisting (optional) True to keep existing selections
57537      */
57538     selectNext : function(keepExisting){
57539         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57540             this.selectRow(this.last+1, keepExisting);
57541             this.grid.getView().focusRow(this.last);
57542         }
57543     },
57544
57545     /**
57546      * Selects the row that precedes the last selected row.
57547      * @param {Boolean} keepExisting (optional) True to keep existing selections
57548      */
57549     selectPrevious : function(keepExisting){
57550         if(this.last){
57551             this.selectRow(this.last-1, keepExisting);
57552             this.grid.getView().focusRow(this.last);
57553         }
57554     },
57555
57556     /**
57557      * Returns the selected records
57558      * @return {Array} Array of selected records
57559      */
57560     getSelections : function(){
57561         return [].concat(this.selections.items);
57562     },
57563
57564     /**
57565      * Returns the first selected record.
57566      * @return {Record}
57567      */
57568     getSelected : function(){
57569         return this.selections.itemAt(0);
57570     },
57571
57572
57573     /**
57574      * Clears all selections.
57575      */
57576     clearSelections : function(fast){
57577         if(this.locked) {
57578             return;
57579         }
57580         if(fast !== true){
57581             var ds = this.grid.dataSource;
57582             var s = this.selections;
57583             s.each(function(r){
57584                 this.deselectRow(ds.indexOfId(r.id));
57585             }, this);
57586             s.clear();
57587         }else{
57588             this.selections.clear();
57589         }
57590         this.last = false;
57591     },
57592
57593
57594     /**
57595      * Selects all rows.
57596      */
57597     selectAll : function(){
57598         if(this.locked) {
57599             return;
57600         }
57601         this.selections.clear();
57602         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57603             this.selectRow(i, true);
57604         }
57605     },
57606
57607     /**
57608      * Returns True if there is a selection.
57609      * @return {Boolean}
57610      */
57611     hasSelection : function(){
57612         return this.selections.length > 0;
57613     },
57614
57615     /**
57616      * Returns True if the specified row is selected.
57617      * @param {Number/Record} record The record or index of the record to check
57618      * @return {Boolean}
57619      */
57620     isSelected : function(index){
57621         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57622         return (r && this.selections.key(r.id) ? true : false);
57623     },
57624
57625     /**
57626      * Returns True if the specified record id is selected.
57627      * @param {String} id The id of record to check
57628      * @return {Boolean}
57629      */
57630     isIdSelected : function(id){
57631         return (this.selections.key(id) ? true : false);
57632     },
57633
57634     // private
57635     handleMouseDown : function(e, t){
57636         var view = this.grid.getView(), rowIndex;
57637         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57638             return;
57639         };
57640         if(e.shiftKey && this.last !== false){
57641             var last = this.last;
57642             this.selectRange(last, rowIndex, e.ctrlKey);
57643             this.last = last; // reset the last
57644             view.focusRow(rowIndex);
57645         }else{
57646             var isSelected = this.isSelected(rowIndex);
57647             if(e.button !== 0 && isSelected){
57648                 view.focusRow(rowIndex);
57649             }else if(e.ctrlKey && isSelected){
57650                 this.deselectRow(rowIndex);
57651             }else if(!isSelected){
57652                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57653                 view.focusRow(rowIndex);
57654             }
57655         }
57656         this.fireEvent("afterselectionchange", this);
57657     },
57658     // private
57659     handleDragableRowClick :  function(grid, rowIndex, e) 
57660     {
57661         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57662             this.selectRow(rowIndex, false);
57663             grid.view.focusRow(rowIndex);
57664              this.fireEvent("afterselectionchange", this);
57665         }
57666     },
57667     
57668     /**
57669      * Selects multiple rows.
57670      * @param {Array} rows Array of the indexes of the row to select
57671      * @param {Boolean} keepExisting (optional) True to keep existing selections
57672      */
57673     selectRows : function(rows, keepExisting){
57674         if(!keepExisting){
57675             this.clearSelections();
57676         }
57677         for(var i = 0, len = rows.length; i < len; i++){
57678             this.selectRow(rows[i], true);
57679         }
57680     },
57681
57682     /**
57683      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57684      * @param {Number} startRow The index of the first row in the range
57685      * @param {Number} endRow The index of the last row in the range
57686      * @param {Boolean} keepExisting (optional) True to retain existing selections
57687      */
57688     selectRange : function(startRow, endRow, keepExisting){
57689         if(this.locked) {
57690             return;
57691         }
57692         if(!keepExisting){
57693             this.clearSelections();
57694         }
57695         if(startRow <= endRow){
57696             for(var i = startRow; i <= endRow; i++){
57697                 this.selectRow(i, true);
57698             }
57699         }else{
57700             for(var i = startRow; i >= endRow; i--){
57701                 this.selectRow(i, true);
57702             }
57703         }
57704     },
57705
57706     /**
57707      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57708      * @param {Number} startRow The index of the first row in the range
57709      * @param {Number} endRow The index of the last row in the range
57710      */
57711     deselectRange : function(startRow, endRow, preventViewNotify){
57712         if(this.locked) {
57713             return;
57714         }
57715         for(var i = startRow; i <= endRow; i++){
57716             this.deselectRow(i, preventViewNotify);
57717         }
57718     },
57719
57720     /**
57721      * Selects a row.
57722      * @param {Number} row The index of the row to select
57723      * @param {Boolean} keepExisting (optional) True to keep existing selections
57724      */
57725     selectRow : function(index, keepExisting, preventViewNotify){
57726         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57727             return;
57728         }
57729         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57730             if(!keepExisting || this.singleSelect){
57731                 this.clearSelections();
57732             }
57733             var r = this.grid.dataSource.getAt(index);
57734             this.selections.add(r);
57735             this.last = this.lastActive = index;
57736             if(!preventViewNotify){
57737                 this.grid.getView().onRowSelect(index);
57738             }
57739             this.fireEvent("rowselect", this, index, r);
57740             this.fireEvent("selectionchange", this);
57741         }
57742     },
57743
57744     /**
57745      * Deselects a row.
57746      * @param {Number} row The index of the row to deselect
57747      */
57748     deselectRow : function(index, preventViewNotify){
57749         if(this.locked) {
57750             return;
57751         }
57752         if(this.last == index){
57753             this.last = false;
57754         }
57755         if(this.lastActive == index){
57756             this.lastActive = false;
57757         }
57758         var r = this.grid.dataSource.getAt(index);
57759         this.selections.remove(r);
57760         if(!preventViewNotify){
57761             this.grid.getView().onRowDeselect(index);
57762         }
57763         this.fireEvent("rowdeselect", this, index);
57764         this.fireEvent("selectionchange", this);
57765     },
57766
57767     // private
57768     restoreLast : function(){
57769         if(this._last){
57770             this.last = this._last;
57771         }
57772     },
57773
57774     // private
57775     acceptsNav : function(row, col, cm){
57776         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57777     },
57778
57779     // private
57780     onEditorKey : function(field, e){
57781         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57782         if(k == e.TAB){
57783             e.stopEvent();
57784             ed.completeEdit();
57785             if(e.shiftKey){
57786                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57787             }else{
57788                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57789             }
57790         }else if(k == e.ENTER && !e.ctrlKey){
57791             e.stopEvent();
57792             ed.completeEdit();
57793             if(e.shiftKey){
57794                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57795             }else{
57796                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57797             }
57798         }else if(k == e.ESC){
57799             ed.cancelEdit();
57800         }
57801         if(newCell){
57802             g.startEditing(newCell[0], newCell[1]);
57803         }
57804     }
57805 });/*
57806  * Based on:
57807  * Ext JS Library 1.1.1
57808  * Copyright(c) 2006-2007, Ext JS, LLC.
57809  *
57810  * Originally Released Under LGPL - original licence link has changed is not relivant.
57811  *
57812  * Fork - LGPL
57813  * <script type="text/javascript">
57814  */
57815 /**
57816  * @class Roo.grid.CellSelectionModel
57817  * @extends Roo.grid.AbstractSelectionModel
57818  * This class provides the basic implementation for cell selection in a grid.
57819  * @constructor
57820  * @param {Object} config The object containing the configuration of this model.
57821  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57822  */
57823 Roo.grid.CellSelectionModel = function(config){
57824     Roo.apply(this, config);
57825
57826     this.selection = null;
57827
57828     this.addEvents({
57829         /**
57830              * @event beforerowselect
57831              * Fires before a cell is selected.
57832              * @param {SelectionModel} this
57833              * @param {Number} rowIndex The selected row index
57834              * @param {Number} colIndex The selected cell index
57835              */
57836             "beforecellselect" : true,
57837         /**
57838              * @event cellselect
57839              * Fires when a cell is selected.
57840              * @param {SelectionModel} this
57841              * @param {Number} rowIndex The selected row index
57842              * @param {Number} colIndex The selected cell index
57843              */
57844             "cellselect" : true,
57845         /**
57846              * @event selectionchange
57847              * Fires when the active selection changes.
57848              * @param {SelectionModel} this
57849              * @param {Object} selection null for no selection or an object (o) with two properties
57850                 <ul>
57851                 <li>o.record: the record object for the row the selection is in</li>
57852                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
57853                 </ul>
57854              */
57855             "selectionchange" : true,
57856         /**
57857              * @event tabend
57858              * Fires when the tab (or enter) was pressed on the last editable cell
57859              * You can use this to trigger add new row.
57860              * @param {SelectionModel} this
57861              */
57862             "tabend" : true,
57863          /**
57864              * @event beforeeditnext
57865              * Fires before the next editable sell is made active
57866              * You can use this to skip to another cell or fire the tabend
57867              *    if you set cell to false
57868              * @param {Object} eventdata object : { cell : [ row, col ] } 
57869              */
57870             "beforeeditnext" : true
57871     });
57872     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
57873 };
57874
57875 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
57876     
57877     enter_is_tab: false,
57878
57879     /** @ignore */
57880     initEvents : function(){
57881         this.grid.on("mousedown", this.handleMouseDown, this);
57882         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
57883         var view = this.grid.view;
57884         view.on("refresh", this.onViewChange, this);
57885         view.on("rowupdated", this.onRowUpdated, this);
57886         view.on("beforerowremoved", this.clearSelections, this);
57887         view.on("beforerowsinserted", this.clearSelections, this);
57888         if(this.grid.isEditor){
57889             this.grid.on("beforeedit", this.beforeEdit,  this);
57890         }
57891     },
57892
57893         //private
57894     beforeEdit : function(e){
57895         this.select(e.row, e.column, false, true, e.record);
57896     },
57897
57898         //private
57899     onRowUpdated : function(v, index, r){
57900         if(this.selection && this.selection.record == r){
57901             v.onCellSelect(index, this.selection.cell[1]);
57902         }
57903     },
57904
57905         //private
57906     onViewChange : function(){
57907         this.clearSelections(true);
57908     },
57909
57910         /**
57911          * Returns the currently selected cell,.
57912          * @return {Array} The selected cell (row, column) or null if none selected.
57913          */
57914     getSelectedCell : function(){
57915         return this.selection ? this.selection.cell : null;
57916     },
57917
57918     /**
57919      * Clears all selections.
57920      * @param {Boolean} true to prevent the gridview from being notified about the change.
57921      */
57922     clearSelections : function(preventNotify){
57923         var s = this.selection;
57924         if(s){
57925             if(preventNotify !== true){
57926                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
57927             }
57928             this.selection = null;
57929             this.fireEvent("selectionchange", this, null);
57930         }
57931     },
57932
57933     /**
57934      * Returns true if there is a selection.
57935      * @return {Boolean}
57936      */
57937     hasSelection : function(){
57938         return this.selection ? true : false;
57939     },
57940
57941     /** @ignore */
57942     handleMouseDown : function(e, t){
57943         var v = this.grid.getView();
57944         if(this.isLocked()){
57945             return;
57946         };
57947         var row = v.findRowIndex(t);
57948         var cell = v.findCellIndex(t);
57949         if(row !== false && cell !== false){
57950             this.select(row, cell);
57951         }
57952     },
57953
57954     /**
57955      * Selects a cell.
57956      * @param {Number} rowIndex
57957      * @param {Number} collIndex
57958      */
57959     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
57960         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
57961             this.clearSelections();
57962             r = r || this.grid.dataSource.getAt(rowIndex);
57963             this.selection = {
57964                 record : r,
57965                 cell : [rowIndex, colIndex]
57966             };
57967             if(!preventViewNotify){
57968                 var v = this.grid.getView();
57969                 v.onCellSelect(rowIndex, colIndex);
57970                 if(preventFocus !== true){
57971                     v.focusCell(rowIndex, colIndex);
57972                 }
57973             }
57974             this.fireEvent("cellselect", this, rowIndex, colIndex);
57975             this.fireEvent("selectionchange", this, this.selection);
57976         }
57977     },
57978
57979         //private
57980     isSelectable : function(rowIndex, colIndex, cm){
57981         return !cm.isHidden(colIndex);
57982     },
57983
57984     /** @ignore */
57985     handleKeyDown : function(e){
57986         //Roo.log('Cell Sel Model handleKeyDown');
57987         if(!e.isNavKeyPress()){
57988             return;
57989         }
57990         var g = this.grid, s = this.selection;
57991         if(!s){
57992             e.stopEvent();
57993             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
57994             if(cell){
57995                 this.select(cell[0], cell[1]);
57996             }
57997             return;
57998         }
57999         var sm = this;
58000         var walk = function(row, col, step){
58001             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58002         };
58003         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58004         var newCell;
58005
58006       
58007
58008         switch(k){
58009             case e.TAB:
58010                 // handled by onEditorKey
58011                 if (g.isEditor && g.editing) {
58012                     return;
58013                 }
58014                 if(e.shiftKey) {
58015                     newCell = walk(r, c-1, -1);
58016                 } else {
58017                     newCell = walk(r, c+1, 1);
58018                 }
58019                 break;
58020             
58021             case e.DOWN:
58022                newCell = walk(r+1, c, 1);
58023                 break;
58024             
58025             case e.UP:
58026                 newCell = walk(r-1, c, -1);
58027                 break;
58028             
58029             case e.RIGHT:
58030                 newCell = walk(r, c+1, 1);
58031                 break;
58032             
58033             case e.LEFT:
58034                 newCell = walk(r, c-1, -1);
58035                 break;
58036             
58037             case e.ENTER:
58038                 
58039                 if(g.isEditor && !g.editing){
58040                    g.startEditing(r, c);
58041                    e.stopEvent();
58042                    return;
58043                 }
58044                 
58045                 
58046              break;
58047         };
58048         if(newCell){
58049             this.select(newCell[0], newCell[1]);
58050             e.stopEvent();
58051             
58052         }
58053     },
58054
58055     acceptsNav : function(row, col, cm){
58056         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58057     },
58058     /**
58059      * Selects a cell.
58060      * @param {Number} field (not used) - as it's normally used as a listener
58061      * @param {Number} e - event - fake it by using
58062      *
58063      * var e = Roo.EventObjectImpl.prototype;
58064      * e.keyCode = e.TAB
58065      *
58066      * 
58067      */
58068     onEditorKey : function(field, e){
58069         
58070         var k = e.getKey(),
58071             newCell,
58072             g = this.grid,
58073             ed = g.activeEditor,
58074             forward = false;
58075         ///Roo.log('onEditorKey' + k);
58076         
58077         
58078         if (this.enter_is_tab && k == e.ENTER) {
58079             k = e.TAB;
58080         }
58081         
58082         if(k == e.TAB){
58083             if(e.shiftKey){
58084                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58085             }else{
58086                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58087                 forward = true;
58088             }
58089             
58090             e.stopEvent();
58091             
58092         } else if(k == e.ENTER &&  !e.ctrlKey){
58093             ed.completeEdit();
58094             e.stopEvent();
58095             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58096         
58097                 } else if(k == e.ESC){
58098             ed.cancelEdit();
58099         }
58100                 
58101         if (newCell) {
58102             var ecall = { cell : newCell, forward : forward };
58103             this.fireEvent('beforeeditnext', ecall );
58104             newCell = ecall.cell;
58105                         forward = ecall.forward;
58106         }
58107                 
58108         if(newCell){
58109             //Roo.log('next cell after edit');
58110             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58111         } else if (forward) {
58112             // tabbed past last
58113             this.fireEvent.defer(100, this, ['tabend',this]);
58114         }
58115     }
58116 });/*
58117  * Based on:
58118  * Ext JS Library 1.1.1
58119  * Copyright(c) 2006-2007, Ext JS, LLC.
58120  *
58121  * Originally Released Under LGPL - original licence link has changed is not relivant.
58122  *
58123  * Fork - LGPL
58124  * <script type="text/javascript">
58125  */
58126  
58127 /**
58128  * @class Roo.grid.EditorGrid
58129  * @extends Roo.grid.Grid
58130  * Class for creating and editable grid.
58131  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58132  * The container MUST have some type of size defined for the grid to fill. The container will be 
58133  * automatically set to position relative if it isn't already.
58134  * @param {Object} dataSource The data model to bind to
58135  * @param {Object} colModel The column model with info about this grid's columns
58136  */
58137 Roo.grid.EditorGrid = function(container, config){
58138     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58139     this.getGridEl().addClass("xedit-grid");
58140
58141     if(!this.selModel){
58142         this.selModel = new Roo.grid.CellSelectionModel();
58143     }
58144
58145     this.activeEditor = null;
58146
58147         this.addEvents({
58148             /**
58149              * @event beforeedit
58150              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58151              * <ul style="padding:5px;padding-left:16px;">
58152              * <li>grid - This grid</li>
58153              * <li>record - The record being edited</li>
58154              * <li>field - The field name being edited</li>
58155              * <li>value - The value for the field being edited.</li>
58156              * <li>row - The grid row index</li>
58157              * <li>column - The grid column index</li>
58158              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58159              * </ul>
58160              * @param {Object} e An edit event (see above for description)
58161              */
58162             "beforeedit" : true,
58163             /**
58164              * @event afteredit
58165              * Fires after a cell is edited. <br />
58166              * <ul style="padding:5px;padding-left:16px;">
58167              * <li>grid - This grid</li>
58168              * <li>record - The record being edited</li>
58169              * <li>field - The field name being edited</li>
58170              * <li>value - The value being set</li>
58171              * <li>originalValue - The original value for the field, before the edit.</li>
58172              * <li>row - The grid row index</li>
58173              * <li>column - The grid column index</li>
58174              * </ul>
58175              * @param {Object} e An edit event (see above for description)
58176              */
58177             "afteredit" : true,
58178             /**
58179              * @event validateedit
58180              * Fires after a cell is edited, but before the value is set in the record. 
58181          * You can use this to modify the value being set in the field, Return false
58182              * to cancel the change. The edit event object has the following properties <br />
58183              * <ul style="padding:5px;padding-left:16px;">
58184          * <li>editor - This editor</li>
58185              * <li>grid - This grid</li>
58186              * <li>record - The record being edited</li>
58187              * <li>field - The field name being edited</li>
58188              * <li>value - The value being set</li>
58189              * <li>originalValue - The original value for the field, before the edit.</li>
58190              * <li>row - The grid row index</li>
58191              * <li>column - The grid column index</li>
58192              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58193              * </ul>
58194              * @param {Object} e An edit event (see above for description)
58195              */
58196             "validateedit" : true
58197         });
58198     this.on("bodyscroll", this.stopEditing,  this);
58199     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58200 };
58201
58202 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58203     /**
58204      * @cfg {Number} clicksToEdit
58205      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58206      */
58207     clicksToEdit: 2,
58208
58209     // private
58210     isEditor : true,
58211     // private
58212     trackMouseOver: false, // causes very odd FF errors
58213
58214     onCellDblClick : function(g, row, col){
58215         this.startEditing(row, col);
58216     },
58217
58218     onEditComplete : function(ed, value, startValue){
58219         this.editing = false;
58220         this.activeEditor = null;
58221         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58222         var r = ed.record;
58223         var field = this.colModel.getDataIndex(ed.col);
58224         var e = {
58225             grid: this,
58226             record: r,
58227             field: field,
58228             originalValue: startValue,
58229             value: value,
58230             row: ed.row,
58231             column: ed.col,
58232             cancel:false,
58233             editor: ed
58234         };
58235         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58236         cell.show();
58237           
58238         if(String(value) !== String(startValue)){
58239             
58240             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58241                 r.set(field, e.value);
58242                 // if we are dealing with a combo box..
58243                 // then we also set the 'name' colum to be the displayField
58244                 if (ed.field.displayField && ed.field.name) {
58245                     r.set(ed.field.name, ed.field.el.dom.value);
58246                 }
58247                 
58248                 delete e.cancel; //?? why!!!
58249                 this.fireEvent("afteredit", e);
58250             }
58251         } else {
58252             this.fireEvent("afteredit", e); // always fire it!
58253         }
58254         this.view.focusCell(ed.row, ed.col);
58255     },
58256
58257     /**
58258      * Starts editing the specified for the specified row/column
58259      * @param {Number} rowIndex
58260      * @param {Number} colIndex
58261      */
58262     startEditing : function(row, col){
58263         this.stopEditing();
58264         if(this.colModel.isCellEditable(col, row)){
58265             this.view.ensureVisible(row, col, true);
58266           
58267             var r = this.dataSource.getAt(row);
58268             var field = this.colModel.getDataIndex(col);
58269             var cell = Roo.get(this.view.getCell(row,col));
58270             var e = {
58271                 grid: this,
58272                 record: r,
58273                 field: field,
58274                 value: r.data[field],
58275                 row: row,
58276                 column: col,
58277                 cancel:false 
58278             };
58279             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58280                 this.editing = true;
58281                 var ed = this.colModel.getCellEditor(col, row);
58282                 
58283                 if (!ed) {
58284                     return;
58285                 }
58286                 if(!ed.rendered){
58287                     ed.render(ed.parentEl || document.body);
58288                 }
58289                 ed.field.reset();
58290                
58291                 cell.hide();
58292                 
58293                 (function(){ // complex but required for focus issues in safari, ie and opera
58294                     ed.row = row;
58295                     ed.col = col;
58296                     ed.record = r;
58297                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58298                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58299                     this.activeEditor = ed;
58300                     var v = r.data[field];
58301                     ed.startEdit(this.view.getCell(row, col), v);
58302                     // combo's with 'displayField and name set
58303                     if (ed.field.displayField && ed.field.name) {
58304                         ed.field.el.dom.value = r.data[ed.field.name];
58305                     }
58306                     
58307                     
58308                 }).defer(50, this);
58309             }
58310         }
58311     },
58312         
58313     /**
58314      * Stops any active editing
58315      */
58316     stopEditing : function(){
58317         if(this.activeEditor){
58318             this.activeEditor.completeEdit();
58319         }
58320         this.activeEditor = null;
58321     },
58322         
58323          /**
58324      * Called to get grid's drag proxy text, by default returns this.ddText.
58325      * @return {String}
58326      */
58327     getDragDropText : function(){
58328         var count = this.selModel.getSelectedCell() ? 1 : 0;
58329         return String.format(this.ddText, count, count == 1 ? '' : 's');
58330     }
58331         
58332 });/*
58333  * Based on:
58334  * Ext JS Library 1.1.1
58335  * Copyright(c) 2006-2007, Ext JS, LLC.
58336  *
58337  * Originally Released Under LGPL - original licence link has changed is not relivant.
58338  *
58339  * Fork - LGPL
58340  * <script type="text/javascript">
58341  */
58342
58343 // private - not really -- you end up using it !
58344 // This is a support class used internally by the Grid components
58345
58346 /**
58347  * @class Roo.grid.GridEditor
58348  * @extends Roo.Editor
58349  * Class for creating and editable grid elements.
58350  * @param {Object} config any settings (must include field)
58351  */
58352 Roo.grid.GridEditor = function(field, config){
58353     if (!config && field.field) {
58354         config = field;
58355         field = Roo.factory(config.field, Roo.form);
58356     }
58357     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58358     field.monitorTab = false;
58359 };
58360
58361 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58362     
58363     /**
58364      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58365      */
58366     
58367     alignment: "tl-tl",
58368     autoSize: "width",
58369     hideEl : false,
58370     cls: "x-small-editor x-grid-editor",
58371     shim:false,
58372     shadow:"frame"
58373 });/*
58374  * Based on:
58375  * Ext JS Library 1.1.1
58376  * Copyright(c) 2006-2007, Ext JS, LLC.
58377  *
58378  * Originally Released Under LGPL - original licence link has changed is not relivant.
58379  *
58380  * Fork - LGPL
58381  * <script type="text/javascript">
58382  */
58383   
58384
58385   
58386 Roo.grid.PropertyRecord = Roo.data.Record.create([
58387     {name:'name',type:'string'},  'value'
58388 ]);
58389
58390
58391 Roo.grid.PropertyStore = function(grid, source){
58392     this.grid = grid;
58393     this.store = new Roo.data.Store({
58394         recordType : Roo.grid.PropertyRecord
58395     });
58396     this.store.on('update', this.onUpdate,  this);
58397     if(source){
58398         this.setSource(source);
58399     }
58400     Roo.grid.PropertyStore.superclass.constructor.call(this);
58401 };
58402
58403
58404
58405 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58406     setSource : function(o){
58407         this.source = o;
58408         this.store.removeAll();
58409         var data = [];
58410         for(var k in o){
58411             if(this.isEditableValue(o[k])){
58412                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58413             }
58414         }
58415         this.store.loadRecords({records: data}, {}, true);
58416     },
58417
58418     onUpdate : function(ds, record, type){
58419         if(type == Roo.data.Record.EDIT){
58420             var v = record.data['value'];
58421             var oldValue = record.modified['value'];
58422             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58423                 this.source[record.id] = v;
58424                 record.commit();
58425                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58426             }else{
58427                 record.reject();
58428             }
58429         }
58430     },
58431
58432     getProperty : function(row){
58433        return this.store.getAt(row);
58434     },
58435
58436     isEditableValue: function(val){
58437         if(val && val instanceof Date){
58438             return true;
58439         }else if(typeof val == 'object' || typeof val == 'function'){
58440             return false;
58441         }
58442         return true;
58443     },
58444
58445     setValue : function(prop, value){
58446         this.source[prop] = value;
58447         this.store.getById(prop).set('value', value);
58448     },
58449
58450     getSource : function(){
58451         return this.source;
58452     }
58453 });
58454
58455 Roo.grid.PropertyColumnModel = function(grid, store){
58456     this.grid = grid;
58457     var g = Roo.grid;
58458     g.PropertyColumnModel.superclass.constructor.call(this, [
58459         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58460         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58461     ]);
58462     this.store = store;
58463     this.bselect = Roo.DomHelper.append(document.body, {
58464         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58465             {tag: 'option', value: 'true', html: 'true'},
58466             {tag: 'option', value: 'false', html: 'false'}
58467         ]
58468     });
58469     Roo.id(this.bselect);
58470     var f = Roo.form;
58471     this.editors = {
58472         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58473         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58474         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58475         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58476         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58477     };
58478     this.renderCellDelegate = this.renderCell.createDelegate(this);
58479     this.renderPropDelegate = this.renderProp.createDelegate(this);
58480 };
58481
58482 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58483     
58484     
58485     nameText : 'Name',
58486     valueText : 'Value',
58487     
58488     dateFormat : 'm/j/Y',
58489     
58490     
58491     renderDate : function(dateVal){
58492         return dateVal.dateFormat(this.dateFormat);
58493     },
58494
58495     renderBool : function(bVal){
58496         return bVal ? 'true' : 'false';
58497     },
58498
58499     isCellEditable : function(colIndex, rowIndex){
58500         return colIndex == 1;
58501     },
58502
58503     getRenderer : function(col){
58504         return col == 1 ?
58505             this.renderCellDelegate : this.renderPropDelegate;
58506     },
58507
58508     renderProp : function(v){
58509         return this.getPropertyName(v);
58510     },
58511
58512     renderCell : function(val){
58513         var rv = val;
58514         if(val instanceof Date){
58515             rv = this.renderDate(val);
58516         }else if(typeof val == 'boolean'){
58517             rv = this.renderBool(val);
58518         }
58519         return Roo.util.Format.htmlEncode(rv);
58520     },
58521
58522     getPropertyName : function(name){
58523         var pn = this.grid.propertyNames;
58524         return pn && pn[name] ? pn[name] : name;
58525     },
58526
58527     getCellEditor : function(colIndex, rowIndex){
58528         var p = this.store.getProperty(rowIndex);
58529         var n = p.data['name'], val = p.data['value'];
58530         
58531         if(typeof(this.grid.customEditors[n]) == 'string'){
58532             return this.editors[this.grid.customEditors[n]];
58533         }
58534         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58535             return this.grid.customEditors[n];
58536         }
58537         if(val instanceof Date){
58538             return this.editors['date'];
58539         }else if(typeof val == 'number'){
58540             return this.editors['number'];
58541         }else if(typeof val == 'boolean'){
58542             return this.editors['boolean'];
58543         }else{
58544             return this.editors['string'];
58545         }
58546     }
58547 });
58548
58549 /**
58550  * @class Roo.grid.PropertyGrid
58551  * @extends Roo.grid.EditorGrid
58552  * This class represents the  interface of a component based property grid control.
58553  * <br><br>Usage:<pre><code>
58554  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58555       
58556  });
58557  // set any options
58558  grid.render();
58559  * </code></pre>
58560   
58561  * @constructor
58562  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58563  * The container MUST have some type of size defined for the grid to fill. The container will be
58564  * automatically set to position relative if it isn't already.
58565  * @param {Object} config A config object that sets properties on this grid.
58566  */
58567 Roo.grid.PropertyGrid = function(container, config){
58568     config = config || {};
58569     var store = new Roo.grid.PropertyStore(this);
58570     this.store = store;
58571     var cm = new Roo.grid.PropertyColumnModel(this, store);
58572     store.store.sort('name', 'ASC');
58573     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58574         ds: store.store,
58575         cm: cm,
58576         enableColLock:false,
58577         enableColumnMove:false,
58578         stripeRows:false,
58579         trackMouseOver: false,
58580         clicksToEdit:1
58581     }, config));
58582     this.getGridEl().addClass('x-props-grid');
58583     this.lastEditRow = null;
58584     this.on('columnresize', this.onColumnResize, this);
58585     this.addEvents({
58586          /**
58587              * @event beforepropertychange
58588              * Fires before a property changes (return false to stop?)
58589              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58590              * @param {String} id Record Id
58591              * @param {String} newval New Value
58592          * @param {String} oldval Old Value
58593              */
58594         "beforepropertychange": true,
58595         /**
58596              * @event propertychange
58597              * Fires after a property changes
58598              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58599              * @param {String} id Record Id
58600              * @param {String} newval New Value
58601          * @param {String} oldval Old Value
58602              */
58603         "propertychange": true
58604     });
58605     this.customEditors = this.customEditors || {};
58606 };
58607 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58608     
58609      /**
58610      * @cfg {Object} customEditors map of colnames=> custom editors.
58611      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58612      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58613      * false disables editing of the field.
58614          */
58615     
58616       /**
58617      * @cfg {Object} propertyNames map of property Names to their displayed value
58618          */
58619     
58620     render : function(){
58621         Roo.grid.PropertyGrid.superclass.render.call(this);
58622         this.autoSize.defer(100, this);
58623     },
58624
58625     autoSize : function(){
58626         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58627         if(this.view){
58628             this.view.fitColumns();
58629         }
58630     },
58631
58632     onColumnResize : function(){
58633         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58634         this.autoSize();
58635     },
58636     /**
58637      * Sets the data for the Grid
58638      * accepts a Key => Value object of all the elements avaiable.
58639      * @param {Object} data  to appear in grid.
58640      */
58641     setSource : function(source){
58642         this.store.setSource(source);
58643         //this.autoSize();
58644     },
58645     /**
58646      * Gets all the data from the grid.
58647      * @return {Object} data  data stored in grid
58648      */
58649     getSource : function(){
58650         return this.store.getSource();
58651     }
58652 });/*
58653   
58654  * Licence LGPL
58655  
58656  */
58657  
58658 /**
58659  * @class Roo.grid.Calendar
58660  * @extends Roo.util.Grid
58661  * This class extends the Grid to provide a calendar widget
58662  * <br><br>Usage:<pre><code>
58663  var grid = new Roo.grid.Calendar("my-container-id", {
58664      ds: myDataStore,
58665      cm: myColModel,
58666      selModel: mySelectionModel,
58667      autoSizeColumns: true,
58668      monitorWindowResize: false,
58669      trackMouseOver: true
58670      eventstore : real data store..
58671  });
58672  // set any options
58673  grid.render();
58674   
58675   * @constructor
58676  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58677  * The container MUST have some type of size defined for the grid to fill. The container will be
58678  * automatically set to position relative if it isn't already.
58679  * @param {Object} config A config object that sets properties on this grid.
58680  */
58681 Roo.grid.Calendar = function(container, config){
58682         // initialize the container
58683         this.container = Roo.get(container);
58684         this.container.update("");
58685         this.container.setStyle("overflow", "hidden");
58686     this.container.addClass('x-grid-container');
58687
58688     this.id = this.container.id;
58689
58690     Roo.apply(this, config);
58691     // check and correct shorthanded configs
58692     
58693     var rows = [];
58694     var d =1;
58695     for (var r = 0;r < 6;r++) {
58696         
58697         rows[r]=[];
58698         for (var c =0;c < 7;c++) {
58699             rows[r][c]= '';
58700         }
58701     }
58702     if (this.eventStore) {
58703         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58704         this.eventStore.on('load',this.onLoad, this);
58705         this.eventStore.on('beforeload',this.clearEvents, this);
58706          
58707     }
58708     
58709     this.dataSource = new Roo.data.Store({
58710             proxy: new Roo.data.MemoryProxy(rows),
58711             reader: new Roo.data.ArrayReader({}, [
58712                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58713     });
58714
58715     this.dataSource.load();
58716     this.ds = this.dataSource;
58717     this.ds.xmodule = this.xmodule || false;
58718     
58719     
58720     var cellRender = function(v,x,r)
58721     {
58722         return String.format(
58723             '<div class="fc-day  fc-widget-content"><div>' +
58724                 '<div class="fc-event-container"></div>' +
58725                 '<div class="fc-day-number">{0}</div>'+
58726                 
58727                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58728             '</div></div>', v);
58729     
58730     }
58731     
58732     
58733     this.colModel = new Roo.grid.ColumnModel( [
58734         {
58735             xtype: 'ColumnModel',
58736             xns: Roo.grid,
58737             dataIndex : 'weekday0',
58738             header : 'Sunday',
58739             renderer : cellRender
58740         },
58741         {
58742             xtype: 'ColumnModel',
58743             xns: Roo.grid,
58744             dataIndex : 'weekday1',
58745             header : 'Monday',
58746             renderer : cellRender
58747         },
58748         {
58749             xtype: 'ColumnModel',
58750             xns: Roo.grid,
58751             dataIndex : 'weekday2',
58752             header : 'Tuesday',
58753             renderer : cellRender
58754         },
58755         {
58756             xtype: 'ColumnModel',
58757             xns: Roo.grid,
58758             dataIndex : 'weekday3',
58759             header : 'Wednesday',
58760             renderer : cellRender
58761         },
58762         {
58763             xtype: 'ColumnModel',
58764             xns: Roo.grid,
58765             dataIndex : 'weekday4',
58766             header : 'Thursday',
58767             renderer : cellRender
58768         },
58769         {
58770             xtype: 'ColumnModel',
58771             xns: Roo.grid,
58772             dataIndex : 'weekday5',
58773             header : 'Friday',
58774             renderer : cellRender
58775         },
58776         {
58777             xtype: 'ColumnModel',
58778             xns: Roo.grid,
58779             dataIndex : 'weekday6',
58780             header : 'Saturday',
58781             renderer : cellRender
58782         }
58783     ]);
58784     this.cm = this.colModel;
58785     this.cm.xmodule = this.xmodule || false;
58786  
58787         
58788           
58789     //this.selModel = new Roo.grid.CellSelectionModel();
58790     //this.sm = this.selModel;
58791     //this.selModel.init(this);
58792     
58793     
58794     if(this.width){
58795         this.container.setWidth(this.width);
58796     }
58797
58798     if(this.height){
58799         this.container.setHeight(this.height);
58800     }
58801     /** @private */
58802         this.addEvents({
58803         // raw events
58804         /**
58805          * @event click
58806          * The raw click event for the entire grid.
58807          * @param {Roo.EventObject} e
58808          */
58809         "click" : true,
58810         /**
58811          * @event dblclick
58812          * The raw dblclick event for the entire grid.
58813          * @param {Roo.EventObject} e
58814          */
58815         "dblclick" : true,
58816         /**
58817          * @event contextmenu
58818          * The raw contextmenu event for the entire grid.
58819          * @param {Roo.EventObject} e
58820          */
58821         "contextmenu" : true,
58822         /**
58823          * @event mousedown
58824          * The raw mousedown event for the entire grid.
58825          * @param {Roo.EventObject} e
58826          */
58827         "mousedown" : true,
58828         /**
58829          * @event mouseup
58830          * The raw mouseup event for the entire grid.
58831          * @param {Roo.EventObject} e
58832          */
58833         "mouseup" : true,
58834         /**
58835          * @event mouseover
58836          * The raw mouseover event for the entire grid.
58837          * @param {Roo.EventObject} e
58838          */
58839         "mouseover" : true,
58840         /**
58841          * @event mouseout
58842          * The raw mouseout event for the entire grid.
58843          * @param {Roo.EventObject} e
58844          */
58845         "mouseout" : true,
58846         /**
58847          * @event keypress
58848          * The raw keypress event for the entire grid.
58849          * @param {Roo.EventObject} e
58850          */
58851         "keypress" : true,
58852         /**
58853          * @event keydown
58854          * The raw keydown event for the entire grid.
58855          * @param {Roo.EventObject} e
58856          */
58857         "keydown" : true,
58858
58859         // custom events
58860
58861         /**
58862          * @event cellclick
58863          * Fires when a cell is clicked
58864          * @param {Grid} this
58865          * @param {Number} rowIndex
58866          * @param {Number} columnIndex
58867          * @param {Roo.EventObject} e
58868          */
58869         "cellclick" : true,
58870         /**
58871          * @event celldblclick
58872          * Fires when a cell is double clicked
58873          * @param {Grid} this
58874          * @param {Number} rowIndex
58875          * @param {Number} columnIndex
58876          * @param {Roo.EventObject} e
58877          */
58878         "celldblclick" : true,
58879         /**
58880          * @event rowclick
58881          * Fires when a row is clicked
58882          * @param {Grid} this
58883          * @param {Number} rowIndex
58884          * @param {Roo.EventObject} e
58885          */
58886         "rowclick" : true,
58887         /**
58888          * @event rowdblclick
58889          * Fires when a row is double clicked
58890          * @param {Grid} this
58891          * @param {Number} rowIndex
58892          * @param {Roo.EventObject} e
58893          */
58894         "rowdblclick" : true,
58895         /**
58896          * @event headerclick
58897          * Fires when a header is clicked
58898          * @param {Grid} this
58899          * @param {Number} columnIndex
58900          * @param {Roo.EventObject} e
58901          */
58902         "headerclick" : true,
58903         /**
58904          * @event headerdblclick
58905          * Fires when a header cell is double clicked
58906          * @param {Grid} this
58907          * @param {Number} columnIndex
58908          * @param {Roo.EventObject} e
58909          */
58910         "headerdblclick" : true,
58911         /**
58912          * @event rowcontextmenu
58913          * Fires when a row is right clicked
58914          * @param {Grid} this
58915          * @param {Number} rowIndex
58916          * @param {Roo.EventObject} e
58917          */
58918         "rowcontextmenu" : true,
58919         /**
58920          * @event cellcontextmenu
58921          * Fires when a cell is right clicked
58922          * @param {Grid} this
58923          * @param {Number} rowIndex
58924          * @param {Number} cellIndex
58925          * @param {Roo.EventObject} e
58926          */
58927          "cellcontextmenu" : true,
58928         /**
58929          * @event headercontextmenu
58930          * Fires when a header is right clicked
58931          * @param {Grid} this
58932          * @param {Number} columnIndex
58933          * @param {Roo.EventObject} e
58934          */
58935         "headercontextmenu" : true,
58936         /**
58937          * @event bodyscroll
58938          * Fires when the body element is scrolled
58939          * @param {Number} scrollLeft
58940          * @param {Number} scrollTop
58941          */
58942         "bodyscroll" : true,
58943         /**
58944          * @event columnresize
58945          * Fires when the user resizes a column
58946          * @param {Number} columnIndex
58947          * @param {Number} newSize
58948          */
58949         "columnresize" : true,
58950         /**
58951          * @event columnmove
58952          * Fires when the user moves a column
58953          * @param {Number} oldIndex
58954          * @param {Number} newIndex
58955          */
58956         "columnmove" : true,
58957         /**
58958          * @event startdrag
58959          * Fires when row(s) start being dragged
58960          * @param {Grid} this
58961          * @param {Roo.GridDD} dd The drag drop object
58962          * @param {event} e The raw browser event
58963          */
58964         "startdrag" : true,
58965         /**
58966          * @event enddrag
58967          * Fires when a drag operation is complete
58968          * @param {Grid} this
58969          * @param {Roo.GridDD} dd The drag drop object
58970          * @param {event} e The raw browser event
58971          */
58972         "enddrag" : true,
58973         /**
58974          * @event dragdrop
58975          * Fires when dragged row(s) are dropped on a valid DD target
58976          * @param {Grid} this
58977          * @param {Roo.GridDD} dd The drag drop object
58978          * @param {String} targetId The target drag drop object
58979          * @param {event} e The raw browser event
58980          */
58981         "dragdrop" : true,
58982         /**
58983          * @event dragover
58984          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58985          * @param {Grid} this
58986          * @param {Roo.GridDD} dd The drag drop object
58987          * @param {String} targetId The target drag drop object
58988          * @param {event} e The raw browser event
58989          */
58990         "dragover" : true,
58991         /**
58992          * @event dragenter
58993          *  Fires when the dragged row(s) first cross another DD target while being dragged
58994          * @param {Grid} this
58995          * @param {Roo.GridDD} dd The drag drop object
58996          * @param {String} targetId The target drag drop object
58997          * @param {event} e The raw browser event
58998          */
58999         "dragenter" : true,
59000         /**
59001          * @event dragout
59002          * Fires when the dragged row(s) leave another DD target while being dragged
59003          * @param {Grid} this
59004          * @param {Roo.GridDD} dd The drag drop object
59005          * @param {String} targetId The target drag drop object
59006          * @param {event} e The raw browser event
59007          */
59008         "dragout" : true,
59009         /**
59010          * @event rowclass
59011          * Fires when a row is rendered, so you can change add a style to it.
59012          * @param {GridView} gridview   The grid view
59013          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59014          */
59015         'rowclass' : true,
59016
59017         /**
59018          * @event render
59019          * Fires when the grid is rendered
59020          * @param {Grid} grid
59021          */
59022         'render' : true,
59023             /**
59024              * @event select
59025              * Fires when a date is selected
59026              * @param {DatePicker} this
59027              * @param {Date} date The selected date
59028              */
59029         'select': true,
59030         /**
59031              * @event monthchange
59032              * Fires when the displayed month changes 
59033              * @param {DatePicker} this
59034              * @param {Date} date The selected month
59035              */
59036         'monthchange': true,
59037         /**
59038              * @event evententer
59039              * Fires when mouse over an event
59040              * @param {Calendar} this
59041              * @param {event} Event
59042              */
59043         'evententer': true,
59044         /**
59045              * @event eventleave
59046              * Fires when the mouse leaves an
59047              * @param {Calendar} this
59048              * @param {event}
59049              */
59050         'eventleave': true,
59051         /**
59052              * @event eventclick
59053              * Fires when the mouse click an
59054              * @param {Calendar} this
59055              * @param {event}
59056              */
59057         'eventclick': true,
59058         /**
59059              * @event eventrender
59060              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59061              * @param {Calendar} this
59062              * @param {data} data to be modified
59063              */
59064         'eventrender': true
59065         
59066     });
59067
59068     Roo.grid.Grid.superclass.constructor.call(this);
59069     this.on('render', function() {
59070         this.view.el.addClass('x-grid-cal'); 
59071         
59072         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59073
59074     },this);
59075     
59076     if (!Roo.grid.Calendar.style) {
59077         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59078             
59079             
59080             '.x-grid-cal .x-grid-col' :  {
59081                 height: 'auto !important',
59082                 'vertical-align': 'top'
59083             },
59084             '.x-grid-cal  .fc-event-hori' : {
59085                 height: '14px'
59086             }
59087              
59088             
59089         }, Roo.id());
59090     }
59091
59092     
59093     
59094 };
59095 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59096     /**
59097      * @cfg {Store} eventStore The store that loads events.
59098      */
59099     eventStore : 25,
59100
59101      
59102     activeDate : false,
59103     startDay : 0,
59104     autoWidth : true,
59105     monitorWindowResize : false,
59106
59107     
59108     resizeColumns : function() {
59109         var col = (this.view.el.getWidth() / 7) - 3;
59110         // loop through cols, and setWidth
59111         for(var i =0 ; i < 7 ; i++){
59112             this.cm.setColumnWidth(i, col);
59113         }
59114     },
59115      setDate :function(date) {
59116         
59117         Roo.log('setDate?');
59118         
59119         this.resizeColumns();
59120         var vd = this.activeDate;
59121         this.activeDate = date;
59122 //        if(vd && this.el){
59123 //            var t = date.getTime();
59124 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59125 //                Roo.log('using add remove');
59126 //                
59127 //                this.fireEvent('monthchange', this, date);
59128 //                
59129 //                this.cells.removeClass("fc-state-highlight");
59130 //                this.cells.each(function(c){
59131 //                   if(c.dateValue == t){
59132 //                       c.addClass("fc-state-highlight");
59133 //                       setTimeout(function(){
59134 //                            try{c.dom.firstChild.focus();}catch(e){}
59135 //                       }, 50);
59136 //                       return false;
59137 //                   }
59138 //                   return true;
59139 //                });
59140 //                return;
59141 //            }
59142 //        }
59143         
59144         var days = date.getDaysInMonth();
59145         
59146         var firstOfMonth = date.getFirstDateOfMonth();
59147         var startingPos = firstOfMonth.getDay()-this.startDay;
59148         
59149         if(startingPos < this.startDay){
59150             startingPos += 7;
59151         }
59152         
59153         var pm = date.add(Date.MONTH, -1);
59154         var prevStart = pm.getDaysInMonth()-startingPos;
59155 //        
59156         
59157         
59158         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59159         
59160         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59161         //this.cells.addClassOnOver('fc-state-hover');
59162         
59163         var cells = this.cells.elements;
59164         var textEls = this.textNodes;
59165         
59166         //Roo.each(cells, function(cell){
59167         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59168         //});
59169         
59170         days += startingPos;
59171
59172         // convert everything to numbers so it's fast
59173         var day = 86400000;
59174         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59175         //Roo.log(d);
59176         //Roo.log(pm);
59177         //Roo.log(prevStart);
59178         
59179         var today = new Date().clearTime().getTime();
59180         var sel = date.clearTime().getTime();
59181         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59182         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59183         var ddMatch = this.disabledDatesRE;
59184         var ddText = this.disabledDatesText;
59185         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59186         var ddaysText = this.disabledDaysText;
59187         var format = this.format;
59188         
59189         var setCellClass = function(cal, cell){
59190             
59191             //Roo.log('set Cell Class');
59192             cell.title = "";
59193             var t = d.getTime();
59194             
59195             //Roo.log(d);
59196             
59197             
59198             cell.dateValue = t;
59199             if(t == today){
59200                 cell.className += " fc-today";
59201                 cell.className += " fc-state-highlight";
59202                 cell.title = cal.todayText;
59203             }
59204             if(t == sel){
59205                 // disable highlight in other month..
59206                 cell.className += " fc-state-highlight";
59207                 
59208             }
59209             // disabling
59210             if(t < min) {
59211                 //cell.className = " fc-state-disabled";
59212                 cell.title = cal.minText;
59213                 return;
59214             }
59215             if(t > max) {
59216                 //cell.className = " fc-state-disabled";
59217                 cell.title = cal.maxText;
59218                 return;
59219             }
59220             if(ddays){
59221                 if(ddays.indexOf(d.getDay()) != -1){
59222                     // cell.title = ddaysText;
59223                    // cell.className = " fc-state-disabled";
59224                 }
59225             }
59226             if(ddMatch && format){
59227                 var fvalue = d.dateFormat(format);
59228                 if(ddMatch.test(fvalue)){
59229                     cell.title = ddText.replace("%0", fvalue);
59230                    cell.className = " fc-state-disabled";
59231                 }
59232             }
59233             
59234             if (!cell.initialClassName) {
59235                 cell.initialClassName = cell.dom.className;
59236             }
59237             
59238             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59239         };
59240
59241         var i = 0;
59242         
59243         for(; i < startingPos; i++) {
59244             cells[i].dayName =  (++prevStart);
59245             Roo.log(textEls[i]);
59246             d.setDate(d.getDate()+1);
59247             
59248             //cells[i].className = "fc-past fc-other-month";
59249             setCellClass(this, cells[i]);
59250         }
59251         
59252         var intDay = 0;
59253         
59254         for(; i < days; i++){
59255             intDay = i - startingPos + 1;
59256             cells[i].dayName =  (intDay);
59257             d.setDate(d.getDate()+1);
59258             
59259             cells[i].className = ''; // "x-date-active";
59260             setCellClass(this, cells[i]);
59261         }
59262         var extraDays = 0;
59263         
59264         for(; i < 42; i++) {
59265             //textEls[i].innerHTML = (++extraDays);
59266             
59267             d.setDate(d.getDate()+1);
59268             cells[i].dayName = (++extraDays);
59269             cells[i].className = "fc-future fc-other-month";
59270             setCellClass(this, cells[i]);
59271         }
59272         
59273         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59274         
59275         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59276         
59277         // this will cause all the cells to mis
59278         var rows= [];
59279         var i =0;
59280         for (var r = 0;r < 6;r++) {
59281             for (var c =0;c < 7;c++) {
59282                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59283             }    
59284         }
59285         
59286         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59287         for(i=0;i<cells.length;i++) {
59288             
59289             this.cells.elements[i].dayName = cells[i].dayName ;
59290             this.cells.elements[i].className = cells[i].className;
59291             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59292             this.cells.elements[i].title = cells[i].title ;
59293             this.cells.elements[i].dateValue = cells[i].dateValue ;
59294         }
59295         
59296         
59297         
59298         
59299         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59300         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59301         
59302         ////if(totalRows != 6){
59303             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59304            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59305        // }
59306         
59307         this.fireEvent('monthchange', this, date);
59308         
59309         
59310     },
59311  /**
59312      * Returns the grid's SelectionModel.
59313      * @return {SelectionModel}
59314      */
59315     getSelectionModel : function(){
59316         if(!this.selModel){
59317             this.selModel = new Roo.grid.CellSelectionModel();
59318         }
59319         return this.selModel;
59320     },
59321
59322     load: function() {
59323         this.eventStore.load()
59324         
59325         
59326         
59327     },
59328     
59329     findCell : function(dt) {
59330         dt = dt.clearTime().getTime();
59331         var ret = false;
59332         this.cells.each(function(c){
59333             //Roo.log("check " +c.dateValue + '?=' + dt);
59334             if(c.dateValue == dt){
59335                 ret = c;
59336                 return false;
59337             }
59338             return true;
59339         });
59340         
59341         return ret;
59342     },
59343     
59344     findCells : function(rec) {
59345         var s = rec.data.start_dt.clone().clearTime().getTime();
59346        // Roo.log(s);
59347         var e= rec.data.end_dt.clone().clearTime().getTime();
59348        // Roo.log(e);
59349         var ret = [];
59350         this.cells.each(function(c){
59351              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59352             
59353             if(c.dateValue > e){
59354                 return ;
59355             }
59356             if(c.dateValue < s){
59357                 return ;
59358             }
59359             ret.push(c);
59360         });
59361         
59362         return ret;    
59363     },
59364     
59365     findBestRow: function(cells)
59366     {
59367         var ret = 0;
59368         
59369         for (var i =0 ; i < cells.length;i++) {
59370             ret  = Math.max(cells[i].rows || 0,ret);
59371         }
59372         return ret;
59373         
59374     },
59375     
59376     
59377     addItem : function(rec)
59378     {
59379         // look for vertical location slot in
59380         var cells = this.findCells(rec);
59381         
59382         rec.row = this.findBestRow(cells);
59383         
59384         // work out the location.
59385         
59386         var crow = false;
59387         var rows = [];
59388         for(var i =0; i < cells.length; i++) {
59389             if (!crow) {
59390                 crow = {
59391                     start : cells[i],
59392                     end :  cells[i]
59393                 };
59394                 continue;
59395             }
59396             if (crow.start.getY() == cells[i].getY()) {
59397                 // on same row.
59398                 crow.end = cells[i];
59399                 continue;
59400             }
59401             // different row.
59402             rows.push(crow);
59403             crow = {
59404                 start: cells[i],
59405                 end : cells[i]
59406             };
59407             
59408         }
59409         
59410         rows.push(crow);
59411         rec.els = [];
59412         rec.rows = rows;
59413         rec.cells = cells;
59414         for (var i = 0; i < cells.length;i++) {
59415             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59416             
59417         }
59418         
59419         
59420     },
59421     
59422     clearEvents: function() {
59423         
59424         if (!this.eventStore.getCount()) {
59425             return;
59426         }
59427         // reset number of rows in cells.
59428         Roo.each(this.cells.elements, function(c){
59429             c.rows = 0;
59430         });
59431         
59432         this.eventStore.each(function(e) {
59433             this.clearEvent(e);
59434         },this);
59435         
59436     },
59437     
59438     clearEvent : function(ev)
59439     {
59440         if (ev.els) {
59441             Roo.each(ev.els, function(el) {
59442                 el.un('mouseenter' ,this.onEventEnter, this);
59443                 el.un('mouseleave' ,this.onEventLeave, this);
59444                 el.remove();
59445             },this);
59446             ev.els = [];
59447         }
59448     },
59449     
59450     
59451     renderEvent : function(ev,ctr) {
59452         if (!ctr) {
59453              ctr = this.view.el.select('.fc-event-container',true).first();
59454         }
59455         
59456          
59457         this.clearEvent(ev);
59458             //code
59459        
59460         
59461         
59462         ev.els = [];
59463         var cells = ev.cells;
59464         var rows = ev.rows;
59465         this.fireEvent('eventrender', this, ev);
59466         
59467         for(var i =0; i < rows.length; i++) {
59468             
59469             cls = '';
59470             if (i == 0) {
59471                 cls += ' fc-event-start';
59472             }
59473             if ((i+1) == rows.length) {
59474                 cls += ' fc-event-end';
59475             }
59476             
59477             //Roo.log(ev.data);
59478             // how many rows should it span..
59479             var cg = this.eventTmpl.append(ctr,Roo.apply({
59480                 fccls : cls
59481                 
59482             }, ev.data) , true);
59483             
59484             
59485             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59486             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59487             cg.on('click', this.onEventClick, this, ev);
59488             
59489             ev.els.push(cg);
59490             
59491             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59492             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59493             //Roo.log(cg);
59494              
59495             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59496             cg.setWidth(ebox.right - sbox.x -2);
59497         }
59498     },
59499     
59500     renderEvents: function()
59501     {   
59502         // first make sure there is enough space..
59503         
59504         if (!this.eventTmpl) {
59505             this.eventTmpl = new Roo.Template(
59506                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59507                     '<div class="fc-event-inner">' +
59508                         '<span class="fc-event-time">{time}</span>' +
59509                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59510                     '</div>' +
59511                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59512                 '</div>'
59513             );
59514                 
59515         }
59516                
59517         
59518         
59519         this.cells.each(function(c) {
59520             //Roo.log(c.select('.fc-day-content div',true).first());
59521             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59522         });
59523         
59524         var ctr = this.view.el.select('.fc-event-container',true).first();
59525         
59526         var cls;
59527         this.eventStore.each(function(ev){
59528             
59529             this.renderEvent(ev);
59530              
59531              
59532         }, this);
59533         this.view.layout();
59534         
59535     },
59536     
59537     onEventEnter: function (e, el,event,d) {
59538         this.fireEvent('evententer', this, el, event);
59539     },
59540     
59541     onEventLeave: function (e, el,event,d) {
59542         this.fireEvent('eventleave', this, el, event);
59543     },
59544     
59545     onEventClick: function (e, el,event,d) {
59546         this.fireEvent('eventclick', this, el, event);
59547     },
59548     
59549     onMonthChange: function () {
59550         this.store.load();
59551     },
59552     
59553     onLoad: function () {
59554         
59555         //Roo.log('calendar onload');
59556 //         
59557         if(this.eventStore.getCount() > 0){
59558             
59559            
59560             
59561             this.eventStore.each(function(d){
59562                 
59563                 
59564                 // FIXME..
59565                 var add =   d.data;
59566                 if (typeof(add.end_dt) == 'undefined')  {
59567                     Roo.log("Missing End time in calendar data: ");
59568                     Roo.log(d);
59569                     return;
59570                 }
59571                 if (typeof(add.start_dt) == 'undefined')  {
59572                     Roo.log("Missing Start time in calendar data: ");
59573                     Roo.log(d);
59574                     return;
59575                 }
59576                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59577                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59578                 add.id = add.id || d.id;
59579                 add.title = add.title || '??';
59580                 
59581                 this.addItem(d);
59582                 
59583              
59584             },this);
59585         }
59586         
59587         this.renderEvents();
59588     }
59589     
59590
59591 });
59592 /*
59593  grid : {
59594                 xtype: 'Grid',
59595                 xns: Roo.grid,
59596                 listeners : {
59597                     render : function ()
59598                     {
59599                         _this.grid = this;
59600                         
59601                         if (!this.view.el.hasClass('course-timesheet')) {
59602                             this.view.el.addClass('course-timesheet');
59603                         }
59604                         if (this.tsStyle) {
59605                             this.ds.load({});
59606                             return; 
59607                         }
59608                         Roo.log('width');
59609                         Roo.log(_this.grid.view.el.getWidth());
59610                         
59611                         
59612                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59613                             '.course-timesheet .x-grid-row' : {
59614                                 height: '80px'
59615                             },
59616                             '.x-grid-row td' : {
59617                                 'vertical-align' : 0
59618                             },
59619                             '.course-edit-link' : {
59620                                 'color' : 'blue',
59621                                 'text-overflow' : 'ellipsis',
59622                                 'overflow' : 'hidden',
59623                                 'white-space' : 'nowrap',
59624                                 'cursor' : 'pointer'
59625                             },
59626                             '.sub-link' : {
59627                                 'color' : 'green'
59628                             },
59629                             '.de-act-sup-link' : {
59630                                 'color' : 'purple',
59631                                 'text-decoration' : 'line-through'
59632                             },
59633                             '.de-act-link' : {
59634                                 'color' : 'red',
59635                                 'text-decoration' : 'line-through'
59636                             },
59637                             '.course-timesheet .course-highlight' : {
59638                                 'border-top-style': 'dashed !important',
59639                                 'border-bottom-bottom': 'dashed !important'
59640                             },
59641                             '.course-timesheet .course-item' : {
59642                                 'font-family'   : 'tahoma, arial, helvetica',
59643                                 'font-size'     : '11px',
59644                                 'overflow'      : 'hidden',
59645                                 'padding-left'  : '10px',
59646                                 'padding-right' : '10px',
59647                                 'padding-top' : '10px' 
59648                             }
59649                             
59650                         }, Roo.id());
59651                                 this.ds.load({});
59652                     }
59653                 },
59654                 autoWidth : true,
59655                 monitorWindowResize : false,
59656                 cellrenderer : function(v,x,r)
59657                 {
59658                     return v;
59659                 },
59660                 sm : {
59661                     xtype: 'CellSelectionModel',
59662                     xns: Roo.grid
59663                 },
59664                 dataSource : {
59665                     xtype: 'Store',
59666                     xns: Roo.data,
59667                     listeners : {
59668                         beforeload : function (_self, options)
59669                         {
59670                             options.params = options.params || {};
59671                             options.params._month = _this.monthField.getValue();
59672                             options.params.limit = 9999;
59673                             options.params['sort'] = 'when_dt';    
59674                             options.params['dir'] = 'ASC';    
59675                             this.proxy.loadResponse = this.loadResponse;
59676                             Roo.log("load?");
59677                             //this.addColumns();
59678                         },
59679                         load : function (_self, records, options)
59680                         {
59681                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59682                                 // if you click on the translation.. you can edit it...
59683                                 var el = Roo.get(this);
59684                                 var id = el.dom.getAttribute('data-id');
59685                                 var d = el.dom.getAttribute('data-date');
59686                                 var t = el.dom.getAttribute('data-time');
59687                                 //var id = this.child('span').dom.textContent;
59688                                 
59689                                 //Roo.log(this);
59690                                 Pman.Dialog.CourseCalendar.show({
59691                                     id : id,
59692                                     when_d : d,
59693                                     when_t : t,
59694                                     productitem_active : id ? 1 : 0
59695                                 }, function() {
59696                                     _this.grid.ds.load({});
59697                                 });
59698                            
59699                            });
59700                            
59701                            _this.panel.fireEvent('resize', [ '', '' ]);
59702                         }
59703                     },
59704                     loadResponse : function(o, success, response){
59705                             // this is overridden on before load..
59706                             
59707                             Roo.log("our code?");       
59708                             //Roo.log(success);
59709                             //Roo.log(response)
59710                             delete this.activeRequest;
59711                             if(!success){
59712                                 this.fireEvent("loadexception", this, o, response);
59713                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59714                                 return;
59715                             }
59716                             var result;
59717                             try {
59718                                 result = o.reader.read(response);
59719                             }catch(e){
59720                                 Roo.log("load exception?");
59721                                 this.fireEvent("loadexception", this, o, response, e);
59722                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59723                                 return;
59724                             }
59725                             Roo.log("ready...");        
59726                             // loop through result.records;
59727                             // and set this.tdate[date] = [] << array of records..
59728                             _this.tdata  = {};
59729                             Roo.each(result.records, function(r){
59730                                 //Roo.log(r.data);
59731                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59732                                     _this.tdata[r.data.when_dt.format('j')] = [];
59733                                 }
59734                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59735                             });
59736                             
59737                             //Roo.log(_this.tdata);
59738                             
59739                             result.records = [];
59740                             result.totalRecords = 6;
59741                     
59742                             // let's generate some duumy records for the rows.
59743                             //var st = _this.dateField.getValue();
59744                             
59745                             // work out monday..
59746                             //st = st.add(Date.DAY, -1 * st.format('w'));
59747                             
59748                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59749                             
59750                             var firstOfMonth = date.getFirstDayOfMonth();
59751                             var days = date.getDaysInMonth();
59752                             var d = 1;
59753                             var firstAdded = false;
59754                             for (var i = 0; i < result.totalRecords ; i++) {
59755                                 //var d= st.add(Date.DAY, i);
59756                                 var row = {};
59757                                 var added = 0;
59758                                 for(var w = 0 ; w < 7 ; w++){
59759                                     if(!firstAdded && firstOfMonth != w){
59760                                         continue;
59761                                     }
59762                                     if(d > days){
59763                                         continue;
59764                                     }
59765                                     firstAdded = true;
59766                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59767                                     row['weekday'+w] = String.format(
59768                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59769                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59770                                                     d,
59771                                                     date.format('Y-m-')+dd
59772                                                 );
59773                                     added++;
59774                                     if(typeof(_this.tdata[d]) != 'undefined'){
59775                                         Roo.each(_this.tdata[d], function(r){
59776                                             var is_sub = '';
59777                                             var deactive = '';
59778                                             var id = r.id;
59779                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59780                                             if(r.parent_id*1>0){
59781                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59782                                                 id = r.parent_id;
59783                                             }
59784                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59785                                                 deactive = 'de-act-link';
59786                                             }
59787                                             
59788                                             row['weekday'+w] += String.format(
59789                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59790                                                     id, //0
59791                                                     r.product_id_name, //1
59792                                                     r.when_dt.format('h:ia'), //2
59793                                                     is_sub, //3
59794                                                     deactive, //4
59795                                                     desc // 5
59796                                             );
59797                                         });
59798                                     }
59799                                     d++;
59800                                 }
59801                                 
59802                                 // only do this if something added..
59803                                 if(added > 0){ 
59804                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59805                                 }
59806                                 
59807                                 
59808                                 // push it twice. (second one with an hour..
59809                                 
59810                             }
59811                             //Roo.log(result);
59812                             this.fireEvent("load", this, o, o.request.arg);
59813                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59814                         },
59815                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59816                     proxy : {
59817                         xtype: 'HttpProxy',
59818                         xns: Roo.data,
59819                         method : 'GET',
59820                         url : baseURL + '/Roo/Shop_course.php'
59821                     },
59822                     reader : {
59823                         xtype: 'JsonReader',
59824                         xns: Roo.data,
59825                         id : 'id',
59826                         fields : [
59827                             {
59828                                 'name': 'id',
59829                                 'type': 'int'
59830                             },
59831                             {
59832                                 'name': 'when_dt',
59833                                 'type': 'string'
59834                             },
59835                             {
59836                                 'name': 'end_dt',
59837                                 'type': 'string'
59838                             },
59839                             {
59840                                 'name': 'parent_id',
59841                                 'type': 'int'
59842                             },
59843                             {
59844                                 'name': 'product_id',
59845                                 'type': 'int'
59846                             },
59847                             {
59848                                 'name': 'productitem_id',
59849                                 'type': 'int'
59850                             },
59851                             {
59852                                 'name': 'guid',
59853                                 'type': 'int'
59854                             }
59855                         ]
59856                     }
59857                 },
59858                 toolbar : {
59859                     xtype: 'Toolbar',
59860                     xns: Roo,
59861                     items : [
59862                         {
59863                             xtype: 'Button',
59864                             xns: Roo.Toolbar,
59865                             listeners : {
59866                                 click : function (_self, e)
59867                                 {
59868                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59869                                     sd.setMonth(sd.getMonth()-1);
59870                                     _this.monthField.setValue(sd.format('Y-m-d'));
59871                                     _this.grid.ds.load({});
59872                                 }
59873                             },
59874                             text : "Back"
59875                         },
59876                         {
59877                             xtype: 'Separator',
59878                             xns: Roo.Toolbar
59879                         },
59880                         {
59881                             xtype: 'MonthField',
59882                             xns: Roo.form,
59883                             listeners : {
59884                                 render : function (_self)
59885                                 {
59886                                     _this.monthField = _self;
59887                                    // _this.monthField.set  today
59888                                 },
59889                                 select : function (combo, date)
59890                                 {
59891                                     _this.grid.ds.load({});
59892                                 }
59893                             },
59894                             value : (function() { return new Date(); })()
59895                         },
59896                         {
59897                             xtype: 'Separator',
59898                             xns: Roo.Toolbar
59899                         },
59900                         {
59901                             xtype: 'TextItem',
59902                             xns: Roo.Toolbar,
59903                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
59904                         },
59905                         {
59906                             xtype: 'Fill',
59907                             xns: Roo.Toolbar
59908                         },
59909                         {
59910                             xtype: 'Button',
59911                             xns: Roo.Toolbar,
59912                             listeners : {
59913                                 click : function (_self, e)
59914                                 {
59915                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59916                                     sd.setMonth(sd.getMonth()+1);
59917                                     _this.monthField.setValue(sd.format('Y-m-d'));
59918                                     _this.grid.ds.load({});
59919                                 }
59920                             },
59921                             text : "Next"
59922                         }
59923                     ]
59924                 },
59925                  
59926             }
59927         };
59928         
59929         *//*
59930  * Based on:
59931  * Ext JS Library 1.1.1
59932  * Copyright(c) 2006-2007, Ext JS, LLC.
59933  *
59934  * Originally Released Under LGPL - original licence link has changed is not relivant.
59935  *
59936  * Fork - LGPL
59937  * <script type="text/javascript">
59938  */
59939  
59940 /**
59941  * @class Roo.LoadMask
59942  * A simple utility class for generically masking elements while loading data.  If the element being masked has
59943  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
59944  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
59945  * element's UpdateManager load indicator and will be destroyed after the initial load.
59946  * @constructor
59947  * Create a new LoadMask
59948  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
59949  * @param {Object} config The config object
59950  */
59951 Roo.LoadMask = function(el, config){
59952     this.el = Roo.get(el);
59953     Roo.apply(this, config);
59954     if(this.store){
59955         this.store.on('beforeload', this.onBeforeLoad, this);
59956         this.store.on('load', this.onLoad, this);
59957         this.store.on('loadexception', this.onLoadException, this);
59958         this.removeMask = false;
59959     }else{
59960         var um = this.el.getUpdateManager();
59961         um.showLoadIndicator = false; // disable the default indicator
59962         um.on('beforeupdate', this.onBeforeLoad, this);
59963         um.on('update', this.onLoad, this);
59964         um.on('failure', this.onLoad, this);
59965         this.removeMask = true;
59966     }
59967 };
59968
59969 Roo.LoadMask.prototype = {
59970     /**
59971      * @cfg {Boolean} removeMask
59972      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
59973      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
59974      */
59975     /**
59976      * @cfg {String} msg
59977      * The text to display in a centered loading message box (defaults to 'Loading...')
59978      */
59979     msg : 'Loading...',
59980     /**
59981      * @cfg {String} msgCls
59982      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
59983      */
59984     msgCls : 'x-mask-loading',
59985
59986     /**
59987      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
59988      * @type Boolean
59989      */
59990     disabled: false,
59991
59992     /**
59993      * Disables the mask to prevent it from being displayed
59994      */
59995     disable : function(){
59996        this.disabled = true;
59997     },
59998
59999     /**
60000      * Enables the mask so that it can be displayed
60001      */
60002     enable : function(){
60003         this.disabled = false;
60004     },
60005     
60006     onLoadException : function()
60007     {
60008         Roo.log(arguments);
60009         
60010         if (typeof(arguments[3]) != 'undefined') {
60011             Roo.MessageBox.alert("Error loading",arguments[3]);
60012         } 
60013         /*
60014         try {
60015             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60016                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60017             }   
60018         } catch(e) {
60019             
60020         }
60021         */
60022     
60023         
60024         
60025         this.el.unmask(this.removeMask);
60026     },
60027     // private
60028     onLoad : function()
60029     {
60030         this.el.unmask(this.removeMask);
60031     },
60032
60033     // private
60034     onBeforeLoad : function(){
60035         if(!this.disabled){
60036             this.el.mask(this.msg, this.msgCls);
60037         }
60038     },
60039
60040     // private
60041     destroy : function(){
60042         if(this.store){
60043             this.store.un('beforeload', this.onBeforeLoad, this);
60044             this.store.un('load', this.onLoad, this);
60045             this.store.un('loadexception', this.onLoadException, this);
60046         }else{
60047             var um = this.el.getUpdateManager();
60048             um.un('beforeupdate', this.onBeforeLoad, this);
60049             um.un('update', this.onLoad, this);
60050             um.un('failure', this.onLoad, this);
60051         }
60052     }
60053 };/*
60054  * Based on:
60055  * Ext JS Library 1.1.1
60056  * Copyright(c) 2006-2007, Ext JS, LLC.
60057  *
60058  * Originally Released Under LGPL - original licence link has changed is not relivant.
60059  *
60060  * Fork - LGPL
60061  * <script type="text/javascript">
60062  */
60063
60064
60065 /**
60066  * @class Roo.XTemplate
60067  * @extends Roo.Template
60068  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60069 <pre><code>
60070 var t = new Roo.XTemplate(
60071         '&lt;select name="{name}"&gt;',
60072                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60073         '&lt;/select&gt;'
60074 );
60075  
60076 // then append, applying the master template values
60077  </code></pre>
60078  *
60079  * Supported features:
60080  *
60081  *  Tags:
60082
60083 <pre><code>
60084       {a_variable} - output encoded.
60085       {a_variable.format:("Y-m-d")} - call a method on the variable
60086       {a_variable:raw} - unencoded output
60087       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60088       {a_variable:this.method_on_template(...)} - call a method on the template object.
60089  
60090 </code></pre>
60091  *  The tpl tag:
60092 <pre><code>
60093         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60094         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60095         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60096         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60097   
60098         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60099         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60100 </code></pre>
60101  *      
60102  */
60103 Roo.XTemplate = function()
60104 {
60105     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60106     if (this.html) {
60107         this.compile();
60108     }
60109 };
60110
60111
60112 Roo.extend(Roo.XTemplate, Roo.Template, {
60113
60114     /**
60115      * The various sub templates
60116      */
60117     tpls : false,
60118     /**
60119      *
60120      * basic tag replacing syntax
60121      * WORD:WORD()
60122      *
60123      * // you can fake an object call by doing this
60124      *  x.t:(test,tesT) 
60125      * 
60126      */
60127     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60128
60129     /**
60130      * compile the template
60131      *
60132      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60133      *
60134      */
60135     compile: function()
60136     {
60137         var s = this.html;
60138      
60139         s = ['<tpl>', s, '</tpl>'].join('');
60140     
60141         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60142             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60143             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60144             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60145             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60146             m,
60147             id     = 0,
60148             tpls   = [];
60149     
60150         while(true == !!(m = s.match(re))){
60151             var forMatch   = m[0].match(nameRe),
60152                 ifMatch   = m[0].match(ifRe),
60153                 execMatch   = m[0].match(execRe),
60154                 namedMatch   = m[0].match(namedRe),
60155                 
60156                 exp  = null, 
60157                 fn   = null,
60158                 exec = null,
60159                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60160                 
60161             if (ifMatch) {
60162                 // if - puts fn into test..
60163                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60164                 if(exp){
60165                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60166                 }
60167             }
60168             
60169             if (execMatch) {
60170                 // exec - calls a function... returns empty if true is  returned.
60171                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60172                 if(exp){
60173                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60174                 }
60175             }
60176             
60177             
60178             if (name) {
60179                 // for = 
60180                 switch(name){
60181                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60182                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60183                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60184                 }
60185             }
60186             var uid = namedMatch ? namedMatch[1] : id;
60187             
60188             
60189             tpls.push({
60190                 id:     namedMatch ? namedMatch[1] : id,
60191                 target: name,
60192                 exec:   exec,
60193                 test:   fn,
60194                 body:   m[1] || ''
60195             });
60196             if (namedMatch) {
60197                 s = s.replace(m[0], '');
60198             } else { 
60199                 s = s.replace(m[0], '{xtpl'+ id + '}');
60200             }
60201             ++id;
60202         }
60203         this.tpls = [];
60204         for(var i = tpls.length-1; i >= 0; --i){
60205             this.compileTpl(tpls[i]);
60206             this.tpls[tpls[i].id] = tpls[i];
60207         }
60208         this.master = tpls[tpls.length-1];
60209         return this;
60210     },
60211     /**
60212      * same as applyTemplate, except it's done to one of the subTemplates
60213      * when using named templates, you can do:
60214      *
60215      * var str = pl.applySubTemplate('your-name', values);
60216      *
60217      * 
60218      * @param {Number} id of the template
60219      * @param {Object} values to apply to template
60220      * @param {Object} parent (normaly the instance of this object)
60221      */
60222     applySubTemplate : function(id, values, parent)
60223     {
60224         
60225         
60226         var t = this.tpls[id];
60227         
60228         
60229         try { 
60230             if(t.test && !t.test.call(this, values, parent)){
60231                 return '';
60232             }
60233         } catch(e) {
60234             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60235             Roo.log(e.toString());
60236             Roo.log(t.test);
60237             return ''
60238         }
60239         try { 
60240             
60241             if(t.exec && t.exec.call(this, values, parent)){
60242                 return '';
60243             }
60244         } catch(e) {
60245             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60246             Roo.log(e.toString());
60247             Roo.log(t.exec);
60248             return ''
60249         }
60250         try {
60251             var vs = t.target ? t.target.call(this, values, parent) : values;
60252             parent = t.target ? values : parent;
60253             if(t.target && vs instanceof Array){
60254                 var buf = [];
60255                 for(var i = 0, len = vs.length; i < len; i++){
60256                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60257                 }
60258                 return buf.join('');
60259             }
60260             return t.compiled.call(this, vs, parent);
60261         } catch (e) {
60262             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60263             Roo.log(e.toString());
60264             Roo.log(t.compiled);
60265             return '';
60266         }
60267     },
60268
60269     compileTpl : function(tpl)
60270     {
60271         var fm = Roo.util.Format;
60272         var useF = this.disableFormats !== true;
60273         var sep = Roo.isGecko ? "+" : ",";
60274         var undef = function(str) {
60275             Roo.log("Property not found :"  + str);
60276             return '';
60277         };
60278         
60279         var fn = function(m, name, format, args)
60280         {
60281             //Roo.log(arguments);
60282             args = args ? args.replace(/\\'/g,"'") : args;
60283             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60284             if (typeof(format) == 'undefined') {
60285                 format= 'htmlEncode';
60286             }
60287             if (format == 'raw' ) {
60288                 format = false;
60289             }
60290             
60291             if(name.substr(0, 4) == 'xtpl'){
60292                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60293             }
60294             
60295             // build an array of options to determine if value is undefined..
60296             
60297             // basically get 'xxxx.yyyy' then do
60298             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60299             //    (function () { Roo.log("Property not found"); return ''; })() :
60300             //    ......
60301             
60302             var udef_ar = [];
60303             var lookfor = '';
60304             Roo.each(name.split('.'), function(st) {
60305                 lookfor += (lookfor.length ? '.': '') + st;
60306                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60307             });
60308             
60309             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60310             
60311             
60312             if(format && useF){
60313                 
60314                 args = args ? ',' + args : "";
60315                  
60316                 if(format.substr(0, 5) != "this."){
60317                     format = "fm." + format + '(';
60318                 }else{
60319                     format = 'this.call("'+ format.substr(5) + '", ';
60320                     args = ", values";
60321                 }
60322                 
60323                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60324             }
60325              
60326             if (args.length) {
60327                 // called with xxyx.yuu:(test,test)
60328                 // change to ()
60329                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60330             }
60331             // raw.. - :raw modifier..
60332             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60333             
60334         };
60335         var body;
60336         // branched to use + in gecko and [].join() in others
60337         if(Roo.isGecko){
60338             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60339                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60340                     "';};};";
60341         }else{
60342             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60343             body.push(tpl.body.replace(/(\r\n|\n)/g,
60344                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60345             body.push("'].join('');};};");
60346             body = body.join('');
60347         }
60348         
60349         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60350        
60351         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60352         eval(body);
60353         
60354         return this;
60355     },
60356
60357     applyTemplate : function(values){
60358         return this.master.compiled.call(this, values, {});
60359         //var s = this.subs;
60360     },
60361
60362     apply : function(){
60363         return this.applyTemplate.apply(this, arguments);
60364     }
60365
60366  });
60367
60368 Roo.XTemplate.from = function(el){
60369     el = Roo.getDom(el);
60370     return new Roo.XTemplate(el.value || el.innerHTML);
60371 };